VPTissue Reference Manual
PTreeFile.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "PTreeFile.h"
21 
22 #include "util/misc/Exception.h"
23 #include "util/misc/log_debug.h"
25 
26 #include <boost/property_tree/ptree.hpp>
27 #include <boost/property_tree/xml_parser.hpp>
28 
29 #include <algorithm>
30 #include <exception>
31 #include <iostream>
32 #include <fstream>
33 #include <string>
34 
35 using namespace std;
36 using namespace boost::property_tree;
37 using namespace boost::property_tree::xml_parser;
38 using namespace SimPT_Sim::Util;
39 
40 // ------------------------------------------------------------------------------
41 // Implementations independent of boost iostreams / zlib conditional
42 // ------------------------------------------------------------------------------
43 namespace SimPT_Sim {
44 namespace Util {
45 
46 bool PTreeFile::IsGzipped(const string& path)
47 {
48  string s = path;
49  transform(s.begin(), s.end(), s.begin(), ::tolower);
50  return s.rfind(".gz") != string::npos;
51 }
52 
53 bool PTreeFile::IsPTreeFile(const string& path)
54 {
55  string s = path;
56  transform(s.begin(), s.end(), s.begin(), ::tolower);
57  return path.rfind(".xml.gz") != string::npos || path.rfind("xml") != string::npos ;
58 }
59 
60 string PTreeFile::GetCompleteSuffix(const string& path)
61 {
62  // pos1 points to position past last "/" (if present) or start of string
63  const auto posa = path.rfind("/");
64  const auto pos1 = (posa != string::npos) ? posa + 1 : 0;
65 
66  // [pos1, pos1 + len1] defines substr up to first "."
67  const auto posb = path.substr(pos1).find(".");
68  const auto len1 = (posb != string::npos) ? path.length() - posb : 0;
69 
70  return path.substr(pos1 + posb + 1, len1);
71 }
72 
73 string PTreeFile::GetBaseNameExcludingLabel(const string& path)
74 {
75  // pos1 points to position past last "/" (if present) or start of string
76  const auto posa = path.rfind("/");
77  const auto pos1 = (posa != string::npos) ? posa + 1 : 0;
78 
79  // [pos1, pos1 + len1] defines substr up to first "."
80  const auto posb = path.substr(pos1).find(".");
81  const auto len1 = (posb != string::npos) ? posb : path.length() - pos1;
82 
83  // [pos1, len2] defines the substr up to last "_"
84  const auto posc = path.substr(pos1, len1).rfind("_");
85  const auto len2 = (posc != string::npos) ? posc : len1;
86 
87  return path.substr(pos1, len2);
88 }
89 
90 string PTreeFile::GetBaseNameIncludingLabel(const string& path)
91 {
92  // pos1 points to position past last "/" (if present) or start of string
93  const auto posa = path.rfind("/");
94  const auto pos1 = (posa != string::npos) ? posa + 1 : 0;
95 
96  // [pos1, pos1 + len1] defines substr up to first "."
97  const auto posb = path.substr(pos1).find(".");
98  const auto len1 = (posb != string::npos) ? posb : path.length() - pos1;
99 
100  return path.substr(pos1, len1);
101 }
102 
103 string PTreeFile::GetLabel(const string& path)
104 {
105  // pos1 points to position past last "/" (if present) or start of string
106  const auto posa = path.rfind("/");
107  const auto pos1 = (posa != string::npos) ? posa + 1 : 0;
108 
109  // [pos1, pos1 + len1] defines substr up to first "."
110  const auto posb = path.substr(pos1).find(".");
111  const auto len1 = (posb != string::npos) ? posb : path.length() - pos1;
112 
113  // [pos1, len2] defines the substr up to last "_"
114  const auto posc = path.substr(pos1, len1).rfind("_");
115  const auto len2 = (posc != string::npos) ? len1 - posc - 1 : 0;
116 
117  return path.substr(pos1 + posc + 1, len2);
118 }
119 
120 void PTreeFile::Read(const string& path, ptree& pt)
121 {
122  if (IsGzipped(path)) {
123  ReadXmlGz(path, pt);
124  } else {
125  ReadXml(path, pt);
126  }
127 }
128 
129 void PTreeFile::ReadXml(const string& path, ptree& pt)
130 {
131  read_xml(path, pt, trim_whitespace);
132 }
133 
134 void PTreeFile::Write(const string& path, const ptree& pt)
135 {
136  if (IsGzipped(path)) {
137  WriteXmlGz(path, pt);
138  } else {
139  WriteXml(path, pt);
140  }
141 }
142 
143 void PTreeFile::WriteXml(const string& path, const ptree& pt)
144 {
145  write_xml(path, pt, locale(), XmlWriterSettings::GetTab());
146 }
147 
148 } // namespace Util
149 } // namespace SimPT_Sim
150 
151 
152 #ifdef USE_BOOST_GZIP
153 
154  // ------------------------------------------------------------------------------
155  // Implementation of ReadXmlGz with Boost iostreams
156  // ------------------------------------------------------------------------------
157  #include <boost/iostreams/copy.hpp>
158  #include <boost/iostreams/device/file.hpp>
159  #include <boost/iostreams/device/file_descriptor.hpp>
160  #include <boost/iostreams/filter/gzip.hpp>
161  #include <boost/iostreams/filtering_stream.hpp>
162  #include <boost/iostreams/filtering_streambuf.hpp>
163  #include <boost/iostreams/traits.hpp>
164 
165  namespace SimPT_Sim {
166  namespace Util {
167 
168  using namespace boost::iostreams;
169 
170  void PTreeFile::Compress(const string& in_path, const string& out_path)
171  {
172  // buffered compression with boost iostreams
173  file_source in(in_path);
174  filtering_ostream out;
175  out.push(gzip_compressor());
176  out.push(file_sink(out_path));
177  copy(in, out);
178  }
179 
180  void PTreeFile::Decompress(const string& in_path, const string& out_path)
181  {
182  // buffered decompression with boost iostreams
183  filtering_istream in;
184  in.push(gzip_decompressor());
185  in.push(file_source(in_path));
186  file_sink out(out_path);
187  copy(in, out);
188  }
189 
190  constexpr bool PTreeFile::IsBoostGzipAvailable()
191  {
192  return true;
193  }
194 
195  void PTreeFile::ReadXmlGz(const string& path, ptree& pt)
196  {
197  filtering_streambuf<input> in_buffer;
198  in_buffer.push(gzip_decompressor());
199  in_buffer.push(file_source(path));
200  filtering_istream ptree_in_stream(in_buffer);
201  read_xml(ptree_in_stream, pt, trim_whitespace);
202  }
203 
204  void PTreeFile::WriteXmlGz(const string& path, const ptree& pt)
205  {
206  filtering_streambuf<output> out_buffer;
207  out_buffer.push(gzip_compressor());
208  out_buffer.push(file_sink(path));
209  filtering_ostream ptree_out_stream(out_buffer);
210  write_xml(ptree_out_stream, pt, XmlWriterSettings::GetTab());
211  }
212 
213  } // namespace Util
214  } // namespace SimPT_Sim
215 
216 #else
217 
218  // ------------------------------------------------------------------------------
219  // Implementation of ReadXmlGz with zlib
220  // ------------------------------------------------------------------------------
221  #include <array>
222  #include "zlib.h"
223 
224  namespace SimPT_Sim {
225  namespace Util {
226 
227  void PTreeFile::Compress(const string& in_path, const string& out_path)
228  {
229  // Move file into memory, parse as ptree, then write as compressed xml
230  ptree pt;
231  ReadXml(in_path, pt);
232  WriteXmlGz(out_path, pt);
233  }
234 
235  void PTreeFile::Decompress(const string& in_path, const string& out_path)
236  {
237  // Move file into memory while decompressing, parse as ptree, then write as xml
238  ptree pt;
239  ReadXmlGz(in_path, pt);
240  WriteXml(out_path, pt);
241  }
242 
243  constexpr bool PTreeFile::IsBoostGzipAvailable()
244  {
245  return false;
246  }
247 
248  void PTreeFile::ReadXmlGz(const string& path, ptree& pt)
249  {
250  // Reads chunks of a file, decompresses them and add them to a string that
251  // represents the uncompressed file. That string is then parsed as boost ptree.
252 
253  ifstream infile;
254  infile.open(path, ifstream::in | ifstream::binary);
255  stringstream ptree_in_stream;
256 
257  if (infile) {
258  const unsigned int in_buffer_size = 1 << 15;
259  const unsigned int out_buffer_size = 1 << 18; // larger because we are decompressing
260 
261  // Allocate zlib input/output buffers on heap so Java doesn't
262  // complain about any stack overflows
263  auto in_buffer = unique_ptr<array<char, in_buffer_size>>(
264  new array<char, in_buffer_size>());
265  auto out_buffer = unique_ptr<array<char, out_buffer_size>>(
266  new array<char, out_buffer_size>());
267 
268  z_stream s;
269  s.zalloc = Z_NULL;
270  s.zfree = Z_NULL;
271  s.opaque = Z_NULL;
272  s.avail_in = 0;
273  s.next_in = Z_NULL;
274 
275  if (inflateInit2(&s, 31) != Z_OK) {
276  infile.close();
277  throw runtime_error("could not initialize zlib inflate stream.");
278  }
279 
280  int status = 0;
281  do {
282  try {
283  infile.read(in_buffer->data(), in_buffer_size); // may throw
284  } catch (exception& e) {
285  inflateEnd(&s);
286  infile.close();
287  throw;
288  }
289  s.avail_in = infile.gcount();
290  if (s.avail_in == 0)
291  break; // EOF
292  s.next_in = (unsigned char*) in_buffer->data();
293 
294  do {
295  s.avail_out = out_buffer_size;
296  s.next_out = (unsigned char*) out_buffer->data();
297 
298  status = inflate(&s, Z_NO_FLUSH);
299  if (status == Z_NEED_DICT || status == Z_DATA_ERROR || status == Z_MEM_ERROR) {
300  inflateEnd(&s);
301  infile.close();
302  throw runtime_error("inflate error.");
303  }
304  unsigned int have = out_buffer_size - s.avail_out;
305 
306  // accumulate output buffer contents in string
307  ptree_in_stream.write(out_buffer->data(), have);
308  if (ptree_in_stream.fail()) {
309  inflateEnd(&s);
310  infile.close();
311  throw runtime_error("stringstream write error.");
312  }
313  } while (s.avail_out == 0);
314 
315  } while (status != Z_STREAM_END);
316 
317  inflateEnd(&s);
318  infile.close();
319 
320  if (status != Z_STREAM_END) {
321  throw runtime_error("data error.");
322  }
323 
324  ptree_in_stream.seekg(0); // "rewind" stream
325 
326  } else {
327  throw runtime_error("could not open file.");
328  }
329 
330  read_xml(ptree_in_stream, pt, trim_whitespace);
331  }
332 
333  void PTreeFile::WriteXmlGz(const string& path, const ptree& pt)
334  {
335  stringstream ptree_out_stream;
336  write_xml(ptree_out_stream, pt, XmlWriterSettings::GetTab());
337 
338  const unsigned int in_buffer_size = 1 << 18;
339  const unsigned int out_buffer_size = 1 << 15; // smaller because we are compressing
340  ofstream outfile(path, ofstream::out | ofstream::binary | ofstream::trunc);
341  if (!outfile) {
342  throw runtime_error("could not open file for writing.");
343  }
344  unsigned char in_buffer[in_buffer_size];
345  unsigned char out_buffer[out_buffer_size];
346 
347  z_stream s;
348  s.zalloc = Z_NULL;
349  s.zfree = Z_NULL;
350  s.opaque = Z_NULL;
351  s.avail_in = 0;
352 
353  if (deflateInit2(&s, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15+16, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
354  outfile.close();
355  throw runtime_error("could not initialize zlib deflate stream.");
356  }
357 
358  int flush;
359  do {
360  try {
361  ptree_out_stream.read((char*) in_buffer, in_buffer_size); // may throw
362  } catch (exception& e) {
363  deflateEnd(&s);
364  outfile.close();
365  throw;
366  }
367  s.avail_in = ptree_out_stream.gcount();
368  s.next_in = in_buffer;
369  flush = ptree_out_stream.eof() ? Z_FINISH : Z_NO_FLUSH;
370  do {
371  s.avail_out = out_buffer_size;
372  s.next_out = out_buffer;
373  deflate(&s, flush);
374  int have = out_buffer_size - s.avail_out;
375  outfile.write((char*) out_buffer, have);
376  if (outfile.fail()) {
377  deflateEnd(&s);
378  outfile.close();
379  throw runtime_error("outfile write error.");
380  }
381  } while (s.avail_out == 0);
382  // done when last data in file processed
383  } while (flush != Z_FINISH);
384 
385  // clean up and return
386  deflateEnd(&s);
387  outfile.close();
388  }
389 
390  } // namespace Util
391  } // namespace SimPT_Sim
392 
393 #endif
STL namespace.
Interface for PTreeFile.
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
Namespace for the core simulator.
Macro defs for debug and logging.
Header file for Exception class.
Xml writer settings class.