1 #include <fstream>
2 #include <iterator>
3 #include <sstream>
4 #include <string>
5 
6 #if HAVE_VTK_ZLIB
7 #include <zlib.h>
8 #endif
9 
10 #include <dune/common/classname.hh>
11 #include <dune/common/version.hh>
12 
13 #include "utility/errors.hh"
14 #include "utility/filesystem.hh"
15 #include "utility/string.hh"
16 
17 namespace Dune {
18 
19 template <class Grid, class Creator, class Field>
read(std::string const & filename,bool fillCreator)20 void VtkReader<Grid,Creator,Field>::read (std::string const& filename, bool fillCreator)
21 {
22   // check whether file exists!
23   if (!Vtk::exists(filename))
24     DUNE_THROW(IOError, "File " << filename << " does not exist!");
25 
26   std::ifstream input(filename, std::ios_base::in | std::ios_base::binary);
27   VTK_ASSERT(input.is_open());
28 
29   std::string ext = Vtk::Path(filename).extension().string();
30   if (ext == ".vtu") {
31     readSerialFileFromStream(input, fillCreator);
32     pieces_.push_back(filename);
33   } else if (ext == ".pvtu") {
34     readParallelFileFromStream(input, comm().rank(), comm().size(), fillCreator);
35   } else {
36     DUNE_THROW(Dune::VtkError, "File has unknown file-extension '" << ext << "'. Allowed are only '.vtu' and '.pvtu'.");
37   }
38   read_ = true;
39 }
40 
41 
42 template <class Grid, class Creator, class Field>
readSerialFileFromStream(std::ifstream & input,bool fillCreator)43 void VtkReader<Grid,Creator,Field>::readSerialFileFromStream (std::ifstream& input, bool fillCreator)
44 {
45   clear();
46   compressor_ = Vtk::CompressorTypes::NONE;
47   Vtk::DataTypes header_type = Vtk::DataTypes::UINT32;
48   std::string data_id = "";
49   std::string data_format = "";
50   Vtk::DataTypes data_type = Vtk::DataTypes::UNKNOWN;
51   unsigned int data_components = 0;
52   std::uint64_t data_offset = 0;
53 
54   Sections section = NO_SECTION;
55   for (std::string line; std::getline(input, line); ) {
56     Vtk::ltrim(line);
57 
58     if (isSection(line, "VTKFile", section)) {
59       bool closed = false;
60       auto attr = parseXml(line, closed);
61 
62       if (!attr["type"].empty())
63         VTK_ASSERT_MSG(attr["type"] == "UnstructuredGrid", "VtkReader supports UnstructuredGrid types");
64       if (!attr["byte_order"].empty())
65         VTK_ASSERT_MSG(attr["byte_order"] == "LittleEndian", "LittleEndian byte order supported");
66 
67       if (attr["header_type"] == "UInt32")
68         header_type = Vtk::DataTypes::UINT32;
69       else if (attr["header_type"] == "UInt64")
70         header_type = Vtk::DataTypes::UINT64;
71 
72       if (attr["compressor"] == "vtkZLibDataCompressor")
73         compressor_ = Vtk::CompressorTypes::ZLIB;
74       else if (attr["compressor"] == "vtkLZ4DataCompressor")
75         compressor_ = Vtk::CompressorTypes::LZ4;
76       else if (attr["compressor"] == "vtkLZMADataCompressor")
77         compressor_ = Vtk::CompressorTypes::LZMA;
78 
79       section = VTK_FILE;
80     }
81     else if (isSection(line, "/VTKFile", section, VTK_FILE)) {
82       section = NO_SECTION;
83       break;
84     }
85     else if (isSection(line, "UnstructuredGrid", section, VTK_FILE))
86       section = UNSTRUCTURED_GRID;
87     else if (isSection(line, "/UnstructuredGrid", section, UNSTRUCTURED_GRID))
88       section = VTK_FILE;
89     else if (isSection(line, "Piece", section, UNSTRUCTURED_GRID)) {
90       bool closed = false;
91       auto attr = parseXml(line, closed);
92 
93       VTK_ASSERT_MSG(attr.count("NumberOfPoints") > 0 && attr.count("NumberOfCells") > 0,
94         "Number of points or cells in file must be > 0");
95       numberOfPoints_ = std::stoul(attr["NumberOfPoints"]);
96       numberOfCells_ = std::stoul(attr["NumberOfCells"]);
97       section = PIECE;
98     }
99     else if (isSection(line, "/Piece", section, PIECE))
100       section = UNSTRUCTURED_GRID;
101     else if (isSection(line, "PointData", section, PIECE))
102       section = POINT_DATA;
103     else if (isSection(line, "/PointData", section, POINT_DATA))
104       section = PIECE;
105     else if (isSection(line, "CellData", section, PIECE))
106       section = CELL_DATA;
107     else if (isSection(line, "/CellData", section, CELL_DATA))
108       section = PIECE;
109     else if (isSection(line, "Points", section, PIECE))
110       section = POINTS;
111     else if (isSection(line, "/Points", section, POINTS))
112       section = PIECE;
113     else if (isSection(line, "Cells", section, PIECE))
114       section = CELLS;
115     else if (isSection(line, "/Cells", section, CELLS))
116       section = PIECE;
117     else if (line.substr(1,9) == "DataArray") {
118       bool closed = false;
119       auto attr = parseXml(line, closed);
120 
121       data_type = Vtk::dataTypeOf(attr["type"]);
122 
123       // Use Section.Name as id
124       data_id = toString(section) + "." + attr["Name"];
125 
126       if (section == POINTS)
127         // In the Points section must only be one DataArray with id=Points
128         data_id = "Points";
129 
130       data_components = 1;
131       if (!attr["NumberOfComponents"].empty())
132         data_components = std::stoul(attr["NumberOfComponents"]);
133 
134       // determine FormatType
135       data_format = Vtk::to_lower(attr["format"]);
136       if (data_format == "appended") {
137         format_ = compressor_ != Vtk::CompressorTypes::NONE ? Vtk::FormatTypes::COMPRESSED : Vtk::FormatTypes::BINARY;
138       } else {
139         format_ = Vtk::FormatTypes::ASCII;
140       }
141 
142       // Offset makes sense in appended mode only
143       data_offset = 0;
144       if (!attr["offset"].empty()) {
145         data_offset = std::stoul(attr["offset"]);
146         VTK_ASSERT_MSG(data_format == "appended", "Attribute 'offset' only supported by appended mode");
147       }
148 
149       // Store attributes of DataArray
150       dataArray_[data_id] = {attr["Name"], data_type, data_components, data_offset, section};
151 
152       // Skip section in appended mode
153       if (data_format == "appended") {
154         if (!closed) {
155           while (std::getline(input, line)) {
156             Vtk::ltrim(line);
157             if (line.substr(1,10) == "/DataArray")
158               break;
159           }
160         }
161         continue;
162       }
163 
164       if (section == POINT_DATA)
165         section = PD_DATA_ARRAY;
166       else if (section == POINTS)
167         section = POINTS_DATA_ARRAY;
168       else if (section == CELL_DATA)
169         section = CD_DATA_ARRAY;
170       else if (section == CELLS)
171         section = CELLS_DATA_ARRAY;
172       else
173         DUNE_THROW(Dune::VtkError, "Wrong section for <DataArray>");
174     }
175     else if (line.substr(1,10) == "/DataArray") {
176       if (section == PD_DATA_ARRAY)
177         section = POINT_DATA;
178       else if (section == POINTS_DATA_ARRAY)
179         section = POINTS;
180       else if (section == CD_DATA_ARRAY)
181         section = CELL_DATA;
182       else if (section == CELLS_DATA_ARRAY)
183         section = CELLS;
184       else
185         DUNE_THROW(Dune::VtkError, "Wrong section for </DataArray>");
186     }
187     else if (isSection(line, "AppendedData", section, VTK_FILE)) {
188       bool closed = false;
189       auto attr = parseXml(line, closed);
190       if (!attr["encoding"].empty())
191         VTK_ASSERT_MSG(attr["encoding"] == "raw", "base64 encoding not supported");
192 
193       offset0_ = findAppendedDataPosition(input);
194       Vtk::mapDataTypes<std::is_floating_point, std::is_integral>(dataArray_["Points"].type, header_type,
195       [&](auto f, auto h) {
196         this->readPointsAppended(f,h,input);
197         this->readCellsAppended(h,input);
198       });
199 
200       // read point and cell data
201       for (auto const& d : dataArray_) {
202         if (d.second.section == POINT_DATA) {
203           Vtk::mapDataTypes<std::is_floating_point, std::is_integral>(d.second.type, header_type,
204           [&](auto f, auto h) {
205             this->readPointDataAppended(f,h,input,d.first);
206           });
207         }
208         else if (d.second.section == CELL_DATA) {
209           Vtk::mapDataTypes<std::is_floating_point, std::is_integral>(d.second.type, header_type,
210           [&](auto f, auto h) {
211             this->readCellDataAppended(f,h,input,d.first);
212           });
213         }
214       }
215 
216       section = NO_SECTION; // finish reading after appended section
217       break;
218     }
219     else if (isSection(line, "/AppendedData", section, APPENDED_DATA))
220       section = VTK_FILE;
221 
222     switch (section) {
223       case PD_DATA_ARRAY:
224         section = readPointData(input, data_id);
225         break;
226       case POINTS_DATA_ARRAY:
227         section = readPoints(input, data_id);
228         break;
229       case CD_DATA_ARRAY:
230         section = readCellData(input, data_id);
231         break;
232       case CELLS_DATA_ARRAY:
233         section = readCells(input, data_id);
234         break;
235       default:
236         break;
237     }
238   }
239 
240   if (section != NO_SECTION)
241     DUNE_THROW(Dune::VtkError, "VTK-File is incomplete. It must end with </VTKFile>!");
242 
243   if (fillCreator)
244     fillGridCreator();
245 }
246 
247 
248 template <class Grid, class Creator, class Field>
readParallelFileFromStream(std::ifstream & input,int,int,bool fillCreator)249 void VtkReader<Grid,Creator,Field>::readParallelFileFromStream (std::ifstream& input, int /* commRank */, int /* commSize */, bool fillCreator)
250 {
251   clear();
252 
253   [[maybe_unused]] Vtk::DataTypes header_type = Vtk::DataTypes::UINT32;
254   compressor_ = Vtk::CompressorTypes::NONE;
255 
256   Sections section = NO_SECTION;
257   for (std::string line; std::getline(input, line); ) {
258     Vtk::ltrim(line);
259 
260     if (isSection(line, "VTKFile", section)) {
261       bool closed = false;
262       auto attr = parseXml(line, closed);
263 
264       if (!attr["type"].empty())
265         VTK_ASSERT_MSG(attr["type"] == "PUnstructuredGrid", "VtkReader supports PUnstructuredGrid types");
266       if (!attr["version"].empty())
267         VTK_ASSERT_MSG(std::stod(attr["version"]) == 1.0, "File format must be 1.0");
268       if (!attr["byte_order"].empty())
269         VTK_ASSERT_MSG(attr["byte_order"] == "LittleEndian", "LittleEndian byte order supported");
270 
271       if (attr["header_type"] == "UInt32")
272         header_type = Vtk::DataTypes::UINT32;
273       else if (attr["header_type"] == "UInt64")
274         header_type = Vtk::DataTypes::UINT64;
275 
276       if (attr["compressor"] == "vtkZLibDataCompressor")
277         compressor_ = Vtk::CompressorTypes::ZLIB;
278       else if (attr["compressor"] == "vtkLZ4DataCompressor")
279         compressor_ = Vtk::CompressorTypes::LZ4;
280       else if (attr["compressor"] == "vtkLZMADataCompressor")
281         compressor_ = Vtk::CompressorTypes::LZMA;
282 
283       section = VTK_FILE;
284     }
285     else if (isSection(line, "/VTKFile", section, VTK_FILE)) {
286       section = NO_SECTION;
287       break;
288     } else if (isSection(line, "PUnstructuredGrid", section, VTK_FILE))
289       section = UNSTRUCTURED_GRID;
290     else if (isSection(line, "/PUnstructuredGrid", section, UNSTRUCTURED_GRID))
291       section = VTK_FILE;
292     else if (isSection(line, "Piece", section, UNSTRUCTURED_GRID)) {
293       bool closed = false;
294       auto attr = parseXml(line, closed);
295 
296       VTK_ASSERT_MSG(attr.count("Source") > 0, "No source files for partitions provided");
297       pieces_.push_back(attr["Source"]);
298     }
299   }
300 
301   VTK_ASSERT_MSG(section == NO_SECTION, "VTK-File is incomplete. It must end with </VTKFile>!");
302 
303   if (fillCreator)
304     fillGridCreator();
305 }
306 
307 
308 // @{ implementation detail
309 /**
310  * Read ASCII data from `input` stream into vector `values`
311  * \param max_size  Upper bound for the number of values
312  * \param section   Current XML section you are reading in
313  * \param parent_section   XML Section to return when current `section` is finished.
314  **/
315 template <class IStream, class T, class Sections>
readDataArray(IStream & input,std::vector<T> & values,std::size_t max_size,Sections section,Sections parent_section)316 Sections readDataArray (IStream& input, std::vector<T>& values, std::size_t max_size,
317                         Sections section, Sections parent_section)
318 {
319   values.reserve(max_size < std::size_t(-1) ? max_size : 0);
320   using S = std::conditional_t<(sizeof(T) <= 1), std::uint16_t, T>; // problem when reading chars as ints
321 
322   std::size_t idx = 0;
323   for (std::string line; std::getline(input, line);) {
324     Vtk::trim(line);
325     if (line.substr(1,10) == "/DataArray")
326       return parent_section;
327     if (line[0] == '<')
328       break;
329 
330     std::istringstream stream(line);
331     S value;
332     for (; stream >> value; idx++)
333       values.push_back(T(value));
334     if (idx >= max_size)
335       break;
336   }
337 
338   return section;
339 }
340 
341 template <class IStream, class Sections>
skipRestOfDataArray(IStream & input,Sections section,Sections parent_section)342 Sections skipRestOfDataArray (IStream& input, Sections section, Sections parent_section)
343 {
344   for (std::string line; std::getline(input, line);) {
345     Vtk::ltrim(line);
346     if (line.substr(1,10) == "/DataArray")
347       return parent_section;
348   }
349 
350   return section;
351 }
352 // @}
353 
354 
355 // Read values stored on the cells with name `name`
356 template <class Grid, class Creator, class Field>
357 typename VtkReader<Grid,Creator,Field>::Sections
readCellData(std::ifstream & input,std::string id)358 VtkReader<Grid,Creator,Field>::readCellData (std::ifstream& input, std::string id)
359 {
360   VTK_ASSERT(numberOfCells_ > 0);
361   unsigned int components = dataArray_[id].components;
362 
363   Sections sec;
364   std::vector<Field>& values = cellData_[id];
365   sec = readDataArray(input, values, components*numberOfCells_, CD_DATA_ARRAY, CELL_DATA);
366   if (sec != CELL_DATA)
367     sec = skipRestOfDataArray(input, CD_DATA_ARRAY, CELL_DATA);
368   VTK_ASSERT(sec == CELL_DATA);
369   VTK_ASSERT(values.size() == components*numberOfCells_);
370 
371   return sec;
372 }
373 
374 
375 template <class Grid, class Creator, class Field>
376   template <class FloatType, class HeaderType>
readCellDataAppended(MetaType<FloatType>,MetaType<HeaderType>,std::ifstream & input,std::string id)377 void VtkReader<Grid,Creator,Field>::readCellDataAppended (MetaType<FloatType>, MetaType<HeaderType>, std::ifstream& input, std::string id)
378 {
379   VTK_ASSERT(numberOfCells_ > 0);
380   unsigned int components = dataArray_[id].components;
381 
382   std::vector<FloatType> values;
383   readAppended(input, values, HeaderType(dataArray_[id].offset));
384   VTK_ASSERT(values.size() == components*numberOfCells_);
385 
386   cellData_[id].resize(values.size());
387   std::copy(values.begin(), values.end(), cellData_[id].begin());
388 }
389 
390 
391 template <class Grid, class Creator, class Field>
392 typename VtkReader<Grid,Creator,Field>::Sections
readPointData(std::ifstream & input,std::string id)393 VtkReader<Grid,Creator,Field>::readPointData (std::ifstream& input, std::string id)
394 {
395   VTK_ASSERT(numberOfPoints_ > 0);
396   unsigned int components = dataArray_[id].components;
397 
398   Sections sec;
399   std::vector<Field>& values = pointData_[id];
400   sec = readDataArray(input, values, components*numberOfPoints_, PD_DATA_ARRAY, POINT_DATA);
401   if (sec != POINT_DATA)
402     sec = skipRestOfDataArray(input, PD_DATA_ARRAY, POINT_DATA);
403   VTK_ASSERT(sec == POINT_DATA);
404   VTK_ASSERT(values.size() == components*numberOfPoints_);
405 
406   return sec;
407 }
408 
409 
410 template <class Grid, class Creator, class Field>
411   template <class FloatType, class HeaderType>
readPointDataAppended(MetaType<FloatType>,MetaType<HeaderType>,std::ifstream & input,std::string id)412 void VtkReader<Grid,Creator,Field>::readPointDataAppended (MetaType<FloatType>, MetaType<HeaderType>, std::ifstream& input, std::string id)
413 {
414   VTK_ASSERT(numberOfPoints_ > 0);
415   unsigned int components = dataArray_[id].components;
416 
417   std::vector<FloatType> values;
418   readAppended(input, values, HeaderType(dataArray_[id].offset));
419   VTK_ASSERT(values.size() == components*numberOfPoints_);
420 
421   pointData_[id].resize(values.size());
422   std::copy(values.begin(), values.end(), pointData_[id].begin());
423 }
424 
425 
426 template <class Grid, class Creator, class Field>
427 typename VtkReader<Grid,Creator,Field>::Sections
readPoints(std::ifstream & input,std::string id)428 VtkReader<Grid,Creator,Field>::readPoints (std::ifstream& input, std::string id)
429 {
430   using T = typename GlobalCoordinate::value_type;
431   VTK_ASSERT(numberOfPoints_ > 0);
432   VTK_ASSERT(id == "Points");
433   VTK_ASSERT(dataArray_["Points"].components == 3u);
434 
435   Sections sec;
436 
437   std::vector<T> point_values;
438   sec = readDataArray(input, point_values, 3*numberOfPoints_, POINTS_DATA_ARRAY, POINTS);
439   if (sec != POINTS)
440     sec = skipRestOfDataArray(input, POINTS_DATA_ARRAY, POINTS);
441   VTK_ASSERT(sec == POINTS);
442   VTK_ASSERT(point_values.size() == 3*numberOfPoints_);
443 
444   // extract points from continuous values
445   GlobalCoordinate p;
446   vec_points.reserve(numberOfPoints_);
447   std::size_t idx = 0;
448   for (std::size_t i = 0; i < numberOfPoints_; ++i) {
449     for (std::size_t j = 0; j < p.size(); ++j)
450       p[j] = point_values[idx++];
451     idx += (3u - p.size());
452     vec_points.push_back(p);
453   }
454 
455   return sec;
456 }
457 
458 
459 template <class Grid, class Creator, class Field>
460   template <class FloatType, class HeaderType>
readPointsAppended(MetaType<FloatType>,MetaType<HeaderType>,std::ifstream & input)461 void VtkReader<Grid,Creator,Field>::readPointsAppended (MetaType<FloatType>, MetaType<HeaderType>, std::ifstream& input)
462 {
463   VTK_ASSERT(numberOfPoints_ > 0);
464   VTK_ASSERT(dataArray_["Points"].components == 3u);
465   std::vector<FloatType> point_values;
466   readAppended(input, point_values, HeaderType(dataArray_["Points"].offset));
467   VTK_ASSERT(point_values.size() == 3*numberOfPoints_);
468 
469   // extract points from continuous values
470   GlobalCoordinate p;
471   vec_points.reserve(numberOfPoints_);
472   std::size_t idx = 0;
473   for (std::size_t i = 0; i < numberOfPoints_; ++i) {
474     for (std::size_t j = 0; j < p.size(); ++j)
475       p[j] = FloatType(point_values[idx++]);
476     idx += (3u - p.size());
477     vec_points.push_back(p);
478   }
479 }
480 
481 
482 template <class Grid, class Creator, class Field>
483 typename VtkReader<Grid,Creator,Field>::Sections
readCells(std::ifstream & input,std::string id)484 VtkReader<Grid,Creator,Field>::readCells (std::ifstream& input, std::string id)
485 {
486   Sections sec = CELLS_DATA_ARRAY;
487 
488   VTK_ASSERT(numberOfCells_ > 0);
489   if (id == "Cells.types") {
490     sec = readDataArray(input, vec_types, numberOfCells_, CELLS_DATA_ARRAY, CELLS);
491     VTK_ASSERT(vec_types.size() == numberOfCells_);
492   } else if (id == "Cells.offsets") {
493     sec = readDataArray(input, vec_offsets, numberOfCells_, CELLS_DATA_ARRAY, CELLS);
494     VTK_ASSERT(vec_offsets.size() == numberOfCells_);
495   } else if (id == "Cells.connectivity") {
496     sec = readDataArray(input, vec_connectivity, std::size_t(-1), CELLS_DATA_ARRAY, CELLS);
497   } else if (id == "Cells.global_point_ids") {
498     sec = readDataArray(input, vec_point_ids, numberOfPoints_, CELLS_DATA_ARRAY, CELLS);
499     VTK_ASSERT(vec_point_ids.size() == numberOfPoints_);
500   }
501 
502   return sec;
503 }
504 
505 
506 template <class Grid, class Creator, class Field>
507   template <class HeaderType>
readCellsAppended(MetaType<HeaderType>,std::ifstream & input)508 void VtkReader<Grid,Creator,Field>::readCellsAppended (MetaType<HeaderType>, std::ifstream& input)
509 {
510   VTK_ASSERT(numberOfCells_ > 0);
511   auto types_data = dataArray_["Cells.types"];
512   auto offsets_data = dataArray_["Cells.offsets"];
513   auto connectivity_data = dataArray_["Cells.connectivity"];
514 
515   VTK_ASSERT(types_data.type == Vtk::DataTypes::UINT8);
516   readAppended(input, vec_types, HeaderType(types_data.offset));
517   VTK_ASSERT(vec_types.size() == numberOfCells_);
518 
519   if (offsets_data.type == Vtk::DataTypes::INT64)
520     readAppended(input, vec_offsets, HeaderType(offsets_data.offset));
521   else if (offsets_data.type == Vtk::DataTypes::INT32) {
522     std::vector<std::int32_t> offsets;
523     readAppended(input, offsets, HeaderType(offsets_data.offset));
524     vec_offsets.resize(offsets.size());
525     std::copy(offsets.begin(), offsets.end(), vec_offsets.begin());
526   }
527   else { DUNE_THROW(Dune::NotImplemented, "Unsupported DataType in Cell offsets."); }
528   VTK_ASSERT(vec_offsets.size() == numberOfCells_);
529 
530   if (connectivity_data.type == Vtk::DataTypes::INT64)
531     readAppended(input, vec_connectivity, HeaderType(connectivity_data.offset));
532   else if (connectivity_data.type == Vtk::DataTypes::INT32) {
533     std::vector<std::int32_t> connectivity;
534     readAppended(input, connectivity, HeaderType(connectivity_data.offset));
535     vec_connectivity.resize(connectivity.size());
536     std::copy(connectivity.begin(), connectivity.end(), vec_connectivity.begin());
537   }
538   else { DUNE_THROW(Dune::NotImplemented, "Unsupported DataType in Cell connectivity."); }
539   VTK_ASSERT(vec_connectivity.size() == std::size_t(vec_offsets.back()));
540 
541   if (dataArray_.count("Cells.global_point_ids") > 0) {
542     auto point_id_data = dataArray_["Cells.global_point_ids"];
543     VTK_ASSERT(point_id_data.type == Vtk::DataTypes::UINT64);
544     readAppended(input, vec_point_ids, HeaderType(point_id_data.offset));
545     VTK_ASSERT(vec_point_ids.size() == numberOfPoints_);
546   }
547 }
548 
549 
550 // @{ implementation detail
551 namespace {
552 
553 /**
554  * Read compressed data into `buffer_in`, uncompress it and store the result in
555  * the concrete-data-type `buffer`
556  * \param bs     Size of the uncompressed data
557  * \param cbs    Size of the compressed data
558  * \param input  Stream to read from.
559  **/
560 template <class T, class IStream>
read_compressed_zlib(T * buffer,unsigned char * buffer_in,std::uint64_t bs,std::uint64_t cbs,IStream & input)561 void read_compressed_zlib (T* buffer, unsigned char* buffer_in,
562                            std::uint64_t bs, std::uint64_t cbs, IStream& input)
563 {
564 #if HAVE_VTK_ZLIB
565   uLongf uncompressed_space = uLongf(bs);
566   uLongf compressed_space = uLongf(cbs);
567 
568   Bytef* compressed_buffer = reinterpret_cast<Bytef*>(buffer_in);
569   Bytef* uncompressed_buffer = reinterpret_cast<Bytef*>(buffer);
570 
571   input.read((char*)(compressed_buffer), compressed_space);
572   VTK_ASSERT(uLongf(input.gcount()) == compressed_space);
573 
574   if (uncompress(uncompressed_buffer, &uncompressed_space, compressed_buffer, compressed_space) != Z_OK) {
575     std::cerr << "Zlib error while uncompressing data.\n";
576     std::abort();
577   }
578   VTK_ASSERT(uLongf(bs) == uncompressed_space);
579 #else
580   std::cerr << "ZLib Compression not supported. Provide the ZLIB package to CMake." << std::endl;
581   std::abort();
582 #endif
583 }
584 
585 template <class T, class IStream>
read_compressed_lz4(T *,unsigned char *,std::uint64_t,std::uint64_t,IStream &)586 void read_compressed_lz4 (T* /* buffer */, unsigned char* /* buffer_in */,
587                           std::uint64_t /* bs */, std::uint64_t /* cbs */, IStream& /* input */)
588 {
589 #if HAVE_VTK_LZ4
590   std::cerr << "LZ4 Compression not yet implemented" << std::endl;
591   std::abort();
592 #else
593   std::cerr << "LZ4 Compression not supported. Provide the LZ4 package to CMake." << std::endl;
594   std::abort();
595 #endif
596 }
597 
598 template <class T, class IStream>
read_compressed_lzma(T *,unsigned char *,std::uint64_t,std::uint64_t,IStream &)599 void read_compressed_lzma (T* /* buffer */, unsigned char* /* buffer_in */,
600                            std::uint64_t /* bs */, std::uint64_t /* cbs */, IStream& /* input */)
601 {
602 #if HAVE_VTK_LZMA
603   std::cerr << "LZMA Compression not yet implemented" << std::endl;
604   std::abort();
605 #else
606   std::cerr << "LZMA Compression not supported. Provide the LZMA package to CMake." << std::endl;
607   std::abort();
608 #endif
609 }
610 
611 }
612 // @}
613 
614 
615 template <class Grid, class Creator, class Field>
616   template <class FloatType, class HeaderType>
readAppended(std::ifstream & input,std::vector<FloatType> & values,HeaderType offset)617 void VtkReader<Grid,Creator,Field>::readAppended (std::ifstream& input, std::vector<FloatType>& values, HeaderType offset)
618 {
619   input.seekg(offset0_ + offset);
620 
621   HeaderType size = 0;
622 
623   HeaderType num_blocks = 0;
624   HeaderType block_size = 0;
625   HeaderType last_block_size = 0;
626   std::vector<HeaderType> cbs; // compressed block sizes
627 
628   // read total size / block-size(s)
629   if (compressor_ != Vtk::CompressorTypes::NONE) {
630     input.read((char*)&num_blocks, sizeof(HeaderType));
631     input.read((char*)&block_size, sizeof(HeaderType));
632     input.read((char*)&last_block_size, sizeof(HeaderType));
633 
634     VTK_ASSERT(block_size % sizeof(FloatType) == 0);
635 
636     // total size of the uncompressed data
637     size = block_size * (num_blocks-1) + last_block_size;
638 
639     // size of the compressed blocks
640     cbs.resize(num_blocks);
641     input.read((char*)cbs.data(), num_blocks*sizeof(HeaderType));
642   } else {
643     input.read((char*)&size, sizeof(HeaderType));
644   }
645   VTK_ASSERT(size > 0 && (size % sizeof(FloatType)) == 0);
646   values.resize(size / sizeof(FloatType));
647 
648   if (compressor_ != Vtk::CompressorTypes::NONE) {
649     // upper bound for compressed block-size
650     HeaderType compressed_block_size = block_size + (block_size + 999)/1000 + 12;
651     // number of values in the full blocks
652     std::size_t num_values = block_size / sizeof(FloatType);
653 
654     std::vector<unsigned char> buffer_in(compressed_block_size);
655     for (std::size_t i = 0; i < std::size_t(num_blocks); ++i) {
656       HeaderType bs = i < std::size_t(num_blocks-1) ? block_size : last_block_size;
657 
658       switch (compressor_) {
659         case Vtk::CompressorTypes::ZLIB:
660           read_compressed_zlib(values.data() + i*num_values, buffer_in.data(), bs, cbs[i], input);
661           break;
662         case Vtk::CompressorTypes::LZ4:
663           read_compressed_lz4(values.data() + i*num_values, buffer_in.data(), bs, cbs[i], input);
664           break;
665         case Vtk::CompressorTypes::LZMA:
666           read_compressed_lzma(values.data() + i*num_values, buffer_in.data(), bs, cbs[i], input);
667           break;
668         default:
669           VTK_ASSERT_MSG(false, "Unsupported Compressor type.");
670           break;
671       }
672     }
673   } else {
674     input.read((char*)(values.data()), size);
675     VTK_ASSERT(input.gcount() == std::streamsize(size));
676   }
677 }
678 
679 
680 template <class Grid, class Creator, class Field>
fillGridCreator(bool insertPieces)681 void VtkReader<Grid,Creator,Field>::fillGridCreator (bool insertPieces)
682 {
683   VTK_ASSERT(vec_points.size() == numberOfPoints_);
684   VTK_ASSERT(vec_types.size() == numberOfCells_);
685   VTK_ASSERT(vec_offsets.size() == numberOfCells_);
686 
687   if (!vec_points.empty())
688     creator_->insertVertices(vec_points, vec_point_ids);
689   if (!vec_types.empty())
690     creator_->insertElements(vec_types, vec_offsets, vec_connectivity);
691   if (insertPieces)
692     creator_->insertPieces(pieces_);
693 }
694 
695 
696 // Convert section into string
697 template <class Grid, class Creator, class Field>
toString(Sections s) const698 std::string VtkReader<Grid,Creator,Field>::toString(Sections s) const
699 {
700   switch (s) {
701     case VTK_FILE:
702       return "VTKFile";
703     case UNSTRUCTURED_GRID:
704       return "UnstructuredGrid";
705     case PIECE:
706       return "Piece";
707     case POINT_DATA:
708       return "PointData";
709     case CELL_DATA:
710       return "CellData";
711     case POINTS:
712       return "Points";
713     case CELLS:
714       return "Cells";
715     case APPENDED_DATA:
716       return "AppendedData";
717     case PD_DATA_ARRAY:
718     case CD_DATA_ARRAY:
719     case POINTS_DATA_ARRAY:
720     case CELLS_DATA_ARRAY:
721       return "DataArray";
722     default:
723       return "Unknown";
724   }
725 }
726 
727 
728 // Assume input already read the line <AppendedData ...>
729 template <class Grid, class Creator, class Field>
findAppendedDataPosition(std::ifstream & input) const730 std::uint64_t VtkReader<Grid,Creator,Field>::findAppendedDataPosition (std::ifstream& input) const
731 {
732   char c;
733   while (input.get(c) && std::isblank(c)) { /*do nothing*/ }
734 
735   std::uint64_t offset = input.tellg();
736   if (c != '_')
737     --offset; // if char is not '_', assume it is part of the data.
738 
739   return offset;
740 }
741 
742 
743 template <class Grid, class Creator, class Field>
parseXml(std::string const & line,bool & closed)744 std::map<std::string, std::string> VtkReader<Grid,Creator,Field>::parseXml (std::string const& line, bool& closed)
745 {
746   closed = false;
747   std::map<std::string, std::string> attr;
748 
749   Sections sec = NO_SECTION;
750   bool escape = false;
751 
752   std::string name = "";
753   std::string value = "";
754   for (auto c : line) {
755     switch (sec) {
756     case NO_SECTION:
757       if (std::isalpha(c) || c == '_') {
758         name.clear();
759         sec = XML_NAME;
760         name.push_back(c);
761       } else if (c == '/') {
762         closed = true;
763       }
764       break;
765     case XML_NAME:
766       if (std::isalpha(c) || c == '_')
767         name.push_back(c);
768       else
769         sec = (c == '=' ? XML_NAME_ASSIGN : NO_SECTION);
770       break;
771     case XML_NAME_ASSIGN:
772       value.clear();
773       escape = false;
774       VTK_ASSERT_MSG( c == '"', "Format error!" );
775       sec = XML_VALUE;
776       break;
777     case XML_VALUE:
778       if (c == '"' && !escape) {
779         attr[name] = value;
780         sec = NO_SECTION;
781       } else if (c == '\\' && !escape) {
782         escape = true;
783       }  else {
784         value.push_back(c);
785         escape = false;
786       }
787       break;
788     default:
789       VTK_ASSERT_MSG(false, "Format error!");
790     }
791   }
792 
793   return attr;
794 }
795 
796 
797 template <class Grid, class Creator, class Field>
clear()798 void VtkReader<Grid,Creator,Field>::clear ()
799 {
800   vec_points.clear();
801   vec_point_ids.clear();
802   vec_types.clear();
803   vec_offsets.clear();
804   vec_connectivity.clear();
805   dataArray_.clear();
806   pieces_.clear();
807 
808   numberOfCells_ = 0;
809   numberOfPoints_ = 0;
810   offset0_ = 0;
811   read_ = false;
812 }
813 
814 } // end namespace Dune
815