| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2000-2022 Inria | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Redistribution and use in source and binary forms, with or without | ||
| 6 | * modification, are permitted provided that the following conditions are met: | ||
| 7 | * | ||
| 8 | * * Redistributions of source code must retain the above copyright notice, | ||
| 9 | * this list of conditions and the following disclaimer. | ||
| 10 | * * Redistributions in binary form must reproduce the above copyright notice, | ||
| 11 | * this list of conditions and the following disclaimer in the documentation | ||
| 12 | * and/or other materials provided with the distribution. | ||
| 13 | * * Neither the name of the ALICE Project-Team nor the names of its | ||
| 14 | * contributors may be used to endorse or promote products derived from this | ||
| 15 | * software without specific prior written permission. | ||
| 16 | * | ||
| 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 27 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | * | ||
| 29 | * Contact: Bruno Levy | ||
| 30 | * | ||
| 31 | * https://www.inria.fr/fr/bruno-levy | ||
| 32 | * | ||
| 33 | * Inria, | ||
| 34 | * Domaine de Voluceau, | ||
| 35 | * 78150 Le Chesnay - Rocquencourt | ||
| 36 | * FRANCE | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | #ifndef GEOGRAM_BASIC_GEOFILE | ||
| 41 | #define GEOGRAM_BASIC_GEOFILE | ||
| 42 | |||
| 43 | #include <geogram/basic/common.h> | ||
| 44 | #include <geogram/basic/numeric.h> | ||
| 45 | #include <geogram/basic/memory.h> | ||
| 46 | #include <geogram/basic/string.h> | ||
| 47 | #ifdef GEOGRAM_USE_BUILTIN_DEPS | ||
| 48 | #include <geogram/third_party/zlib/zlib.h> | ||
| 49 | #else | ||
| 50 | #include <zlib.h> | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #include <stdexcept> | ||
| 54 | #include <fstream> | ||
| 55 | #include <map> | ||
| 56 | |||
| 57 | |||
| 58 | /** | ||
| 59 | * \file geogram/basic/geofile.h | ||
| 60 | * \brief Functions to read and write structured files | ||
| 61 | */ | ||
| 62 | |||
| 63 | namespace GEO { | ||
| 64 | |||
| 65 | /** | ||
| 66 | * \brief GeoFile exception. | ||
| 67 | * \details This exception is thrown by GeoFile functions | ||
| 68 | * whenever a file cannot be written or read. | ||
| 69 | */ | ||
| 70 | class GEOGRAM_API GeoFileException : public std::logic_error { | ||
| 71 | public: | ||
| 72 | /** | ||
| 73 | * \brief GeoFileException constructor | ||
| 74 | * \param[in] s a const reference to the message to be | ||
| 75 | * memorized in the exception. | ||
| 76 | */ | ||
| 77 | ✗ | GeoFileException(const std::string& s) : logic_error(s) { | |
| 78 | ✗ | } | |
| 79 | |||
| 80 | /** | ||
| 81 | * \brief GeoFileException copy constructor. | ||
| 82 | * \param[in] rhs a const reference to the GeoFileException to be | ||
| 83 | * copied. | ||
| 84 | */ | ||
| 85 | GeoFileException(const GeoFileException& rhs) : logic_error(rhs) { | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * \brief GeoFileException destructor. | ||
| 90 | */ | ||
| 91 | ~GeoFileException() GEO_NOEXCEPT override; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /**************************************************************/ | ||
| 95 | |||
| 96 | /** | ||
| 97 | * \brief Reads an ASCII attribute from a file. | ||
| 98 | * \param[in] file the input file, obtained through fopen() | ||
| 99 | * \param[out] base_addr an array with sufficient space for | ||
| 100 | * storing nb_elements of type \p T | ||
| 101 | * \param[in] nb_elements the number of elements to be read | ||
| 102 | * \retval true on success | ||
| 103 | * \retval false otherwise | ||
| 104 | * \tparam T the type of the elements to be read | ||
| 105 | */ | ||
| 106 | 8 | template <class T> inline bool read_ascii_attribute( | |
| 107 | FILE* file, Memory::pointer base_addr, index_t nb_elements | ||
| 108 | ) { | ||
| 109 | 8 | T* attrib = reinterpret_cast<T*>(base_addr); | |
| 110 |
3/4✓ Branch 1 taken 276 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 276 times.
✓ Branch 4 taken 4 times.
|
1112 | for(index_t i=0; i<nb_elements; ++i) { |
| 111 | 552 | std::string buff; | |
| 112 | int res; | ||
| 113 |
3/4✓ Branch 1 taken 1147 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 871 times.
✓ Branch 4 taken 276 times.
|
2294 | while(char(res = fgetc(file)) != '\n') { |
| 114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 871 times.
|
1742 | if(res == EOF) { |
| 115 | ✗ | return false; | |
| 116 | } | ||
| 117 |
1/2✓ Branch 1 taken 871 times.
✗ Branch 2 not taken.
|
1742 | buff.push_back(char(res)); |
| 118 | } | ||
| 119 |
3/4✓ Branch 2 taken 120 times.
✓ Branch 3 taken 156 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 120 times.
|
552 | if(!String::from_string(buff.c_str(),attrib[i])) { |
| 120 | ✗ | return false; | |
| 121 | } | ||
| 122 | } | ||
| 123 | 8 | return true; | |
| 124 | } | ||
| 125 | |||
| 126 | /** | ||
| 127 | * \brief Writes an ASCII attribute to a file. | ||
| 128 | * \param[in] file the output file, obtained through fopen() | ||
| 129 | * \param[in] base_addr an array with nb_elements of type \p T | ||
| 130 | * \param[in] nb_elements the number of elements to be written | ||
| 131 | * \retval true on success | ||
| 132 | * \retval false otherwise | ||
| 133 | * \tparam T the type of the elements to be written | ||
| 134 | */ | ||
| 135 | 8 | template <class T> inline bool write_ascii_attribute( | |
| 136 | FILE* file, Memory::const_pointer base_addr, index_t nb_elements | ||
| 137 | ) { | ||
| 138 | 8 | const T* attrib = reinterpret_cast<const T*>(base_addr); | |
| 139 |
2/2✓ Branch 0 taken 276 times.
✓ Branch 1 taken 4 times.
|
560 | for(index_t i=0; i<nb_elements; ++i) { |
| 140 | 552 | if( | |
| 141 |
1/2✓ Branch 2 taken 276 times.
✗ Branch 3 not taken.
|
552 | fprintf( |
| 142 |
2/4✓ Branch 1 taken 276 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 276 times.
|
1104 | file, "%s\n", String::to_string(attrib[i]).c_str() |
| 143 | 552 | ) == 0 | |
| 144 | ) { | ||
| 145 | ✗ | return false; | |
| 146 | } | ||
| 147 | } | ||
| 148 | 8 | return true; | |
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * \brief Reads an ASCII attribute from a file. | ||
| 153 | * \details Template specialization for char, needed because we | ||
| 154 | * want chars to appear as integers in ASCII files. | ||
| 155 | * \param[in] file the input file, obtained through fopen() | ||
| 156 | * \param[out] base_addr an array with sufficient space for | ||
| 157 | * storing nb_elements of type char | ||
| 158 | * \param[in] nb_elements the number of elements to be read | ||
| 159 | * \retval true on success | ||
| 160 | * \retval false otherwise | ||
| 161 | */ | ||
| 162 | ✗ | template <> inline bool read_ascii_attribute<char>( | |
| 163 | FILE* file, Memory::pointer base_addr, index_t nb_elements | ||
| 164 | ) { | ||
| 165 | ✗ | char* attrib = reinterpret_cast<char*>(base_addr); | |
| 166 | ✗ | for(index_t i=0; i<nb_elements; ++i) { | |
| 167 | int val; | ||
| 168 | ✗ | if(fscanf(file, "%d", &val) == 0) { | |
| 169 | ✗ | return false; | |
| 170 | } | ||
| 171 | ✗ | attrib[i] = char(val); | |
| 172 | } | ||
| 173 | ✗ | return true; | |
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * \brief Writes an ASCII attribute to a file. | ||
| 178 | * \details Template specialization for char, needed because we | ||
| 179 | * want chars to appear as integers in ASCII files. | ||
| 180 | * \param[in] file the output file, obtained through fopen() | ||
| 181 | * \param[in] base_addr an array with nb_elements of type char | ||
| 182 | * \param[in] nb_elements the number of elements to be written | ||
| 183 | * \retval true on success | ||
| 184 | * \retval false otherwise | ||
| 185 | */ | ||
| 186 | ✗ | template <> inline bool write_ascii_attribute<char>( | |
| 187 | FILE* file, Memory::const_pointer base_addr, index_t nb_elements | ||
| 188 | ) { | ||
| 189 | ✗ | const char* attrib = reinterpret_cast<const char*>(base_addr); | |
| 190 | ✗ | for(index_t i=0; i<nb_elements; ++i) { | |
| 191 | ✗ | if(fprintf(file, "%d\n", int(attrib[i])) == 0) { | |
| 192 | ✗ | return false; | |
| 193 | } | ||
| 194 | } | ||
| 195 | ✗ | return true; | |
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * \brief Reads an ASCII attribute from a file. | ||
| 200 | * \details Template specialization for bool. | ||
| 201 | * \param[in] file the input file, obtained through fopen() | ||
| 202 | * \param[out] base_addr an array with sufficient space for | ||
| 203 | * storing nb_elements of type bool (1 byte per element) | ||
| 204 | * \param[in] nb_elements the number of elements to be read | ||
| 205 | * \retval true on success | ||
| 206 | * \retval false otherwise | ||
| 207 | */ | ||
| 208 | ✗ | template <> inline bool read_ascii_attribute<bool>( | |
| 209 | FILE* file, Memory::pointer base_addr, index_t nb_elements | ||
| 210 | ) { | ||
| 211 | ✗ | char* attrib = reinterpret_cast<char*>(base_addr); | |
| 212 | ✗ | for(index_t i=0; i<nb_elements; ++i) { | |
| 213 | int val; | ||
| 214 | ✗ | if(fscanf(file, "%d", &val) == 0) { | |
| 215 | ✗ | return false; | |
| 216 | } | ||
| 217 | ✗ | attrib[i] = char(val); | |
| 218 | } | ||
| 219 | ✗ | return true; | |
| 220 | } | ||
| 221 | |||
| 222 | /** | ||
| 223 | * \brief Writes an ASCII attribute to a file. | ||
| 224 | * \details Template specialization for bool. | ||
| 225 | * \param[in] file the output file, obtained through fopen() | ||
| 226 | * \param[in] base_addr an array with nb_elements of type bool | ||
| 227 | * (1 byte per element) | ||
| 228 | * \param[in] nb_elements the number of elements to be written | ||
| 229 | * \retval true on success | ||
| 230 | * \retval false otherwise | ||
| 231 | */ | ||
| 232 | ✗ | template <> inline bool write_ascii_attribute<bool>( | |
| 233 | FILE* file, Memory::const_pointer base_addr, index_t nb_elements | ||
| 234 | ) { | ||
| 235 | ✗ | const char* attrib = reinterpret_cast<const char*>(base_addr); | |
| 236 | ✗ | for(index_t i=0; i<nb_elements; ++i) { | |
| 237 | ✗ | if(fprintf(file, "%d\n", int(attrib[i])) == 0) { | |
| 238 | ✗ | return false; | |
| 239 | } | ||
| 240 | } | ||
| 241 | ✗ | return true; | |
| 242 | } | ||
| 243 | |||
| 244 | /**************************************************************/ | ||
| 245 | |||
| 246 | /** | ||
| 247 | * \brief Base class for reading or writing Geogram structured binary | ||
| 248 | * files. | ||
| 249 | * \details Geogram structured binary files are organized into "chunks", | ||
| 250 | * in a way inspired by the Interchange File Format (IFF), with | ||
| 251 | * several differences (GeoFile uses little endian and does not use | ||
| 252 | * the standard IFF chunks). Like in IFF, each chunk starts with a | ||
| 253 | * four characters code (FourCC) and its size in bytes, stored in | ||
| 254 | * a 4 bytes unsigned integer. This makes it possible to easily skip | ||
| 255 | * the chunks that are not needed / not understood by the software. | ||
| 256 | * In addition, structured binary files are (optionally) | ||
| 257 | * compressed, using ZLib. Structured files can also be saved/loaded in | ||
| 258 | * ASCII, human-readable form. Natively, GeoFile uses the following chunks: | ||
| 259 | * - CMNT (Comment): contains a string | ||
| 260 | * - CMDL (Command Line): contains a vector of string | ||
| 261 | * - EOFL (End of file): an end of file marker. Can be used to | ||
| 262 | * indicate the boundaries of multiple objects stored in the | ||
| 263 | * same file | ||
| 264 | * - HEAD (Geofile header): contains the string GEOGRAM and a | ||
| 265 | * version string | ||
| 266 | * - PSET (Property Set): a set of properties. For instance, in | ||
| 267 | * a mesh, each mesh element type (vertices, edges, facets...) | ||
| 268 | * corresponds to a property set. | ||
| 269 | * - PROP (Property): a property attached to Property Set. | ||
| 270 | * - SPTR (Separator): marks the boundaries between multiple objects | ||
| 271 | * stored in the same GeoFile | ||
| 272 | */ | ||
| 273 | class GEOGRAM_API GeoFile { | ||
| 274 | public: | ||
| 275 | |||
| 276 | /** | ||
| 277 | * \brief The function pointer type for reading and writing attributes | ||
| 278 | * in ASCII files. | ||
| 279 | */ | ||
| 280 | typedef bool (*AsciiAttributeReadSerializer)( | ||
| 281 | FILE* file, Memory::pointer base_address, index_t nb_elements | ||
| 282 | ); | ||
| 283 | |||
| 284 | typedef bool (*AsciiAttributeWriteSerializer)( | ||
| 285 | FILE* file, Memory::const_pointer base_address, index_t nb_elements | ||
| 286 | ); | ||
| 287 | |||
| 288 | /** | ||
| 289 | * \brief Declares a new attribute type that can be read from | ||
| 290 | * and written to ascii files. | ||
| 291 | * \param[in] type_name the C++ type name of the attribute | ||
| 292 | * \param[in] read the function pointer for reading an attribute | ||
| 293 | * \param[in] write the function pointer for writing an attribute | ||
| 294 | */ | ||
| 295 | static void register_ascii_attribute_serializer( | ||
| 296 | const std::string& type_name, | ||
| 297 | AsciiAttributeReadSerializer read, | ||
| 298 | AsciiAttributeWriteSerializer write | ||
| 299 | ); | ||
| 300 | |||
| 301 | /** | ||
| 302 | * \brief GeoFile constructor. | ||
| 303 | * \param[in] filename a const reference to the file name. | ||
| 304 | */ | ||
| 305 | GeoFile(const std::string& filename); | ||
| 306 | |||
| 307 | /** | ||
| 308 | * \brief GeoFile destructor. | ||
| 309 | */ | ||
| 310 | ~GeoFile(); | ||
| 311 | |||
| 312 | /** | ||
| 313 | * \brief Tests whether this GeoFile is ascii. | ||
| 314 | * \details GeoFile can be ascii or binary. If file name | ||
| 315 | * ends with "_ascii", then GeoFile is ascii. | ||
| 316 | * \retval true if this GeoFile is ascii | ||
| 317 | * \retval false otherwise | ||
| 318 | */ | ||
| 319 | bool is_ascii() const { | ||
| 320 | return ascii_; | ||
| 321 | } | ||
| 322 | |||
| 323 | /** | ||
| 324 | * \brief Tests whether this GeoFile is in GARGANTUA mode. | ||
| 325 | * \details In GARGANTUA mode, index_t is 64 bits wide, else it is 32 bits. | ||
| 326 | * geogram can be compiled in standard or GARGANTUA mode. When loading a | ||
| 327 | * GARGANTUA file in standard mode, each time an index_t is read, it is | ||
| 328 | * checked whether it fits in 32 bits. An exception is thrown if it is not | ||
| 329 | * the case. | ||
| 330 | * \retval true if this file is in GARGANTUA mode | ||
| 331 | * \retval false otherwise | ||
| 332 | */ | ||
| 333 | ✗ | bool gargantua_mode() const { | |
| 334 | ✗ | return gargantua_mode_; | |
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * \brief Gets the current chunk class. | ||
| 339 | * \return the current chunk class | ||
| 340 | */ | ||
| 341 | ✗ | const std::string& current_chunk_class() const { | |
| 342 | ✗ | return current_chunk_class_; | |
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * \brief Gets the size of the current chunk. | ||
| 347 | * \return the size of the current chunk, in bytes | ||
| 348 | */ | ||
| 349 | ✗ | size_t current_chunk_size() const { | |
| 350 | ✗ | return current_chunk_size_; | |
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * \brief Internal representation of attributes. | ||
| 355 | */ | ||
| 356 | struct AttributeInfo { | ||
| 357 | |||
| 358 | /** | ||
| 359 | * \brief AttributeInfo constructor. | ||
| 360 | */ | ||
| 361 | AttributeInfo() : element_size(0), dimension(0) { | ||
| 362 | } | ||
| 363 | |||
| 364 | /** | ||
| 365 | * \brief AttributeInfo constructor. | ||
| 366 | * \param[in] name_in name of the attribute | ||
| 367 | * \param[in] element_type_in C++ type of the elements, as a string | ||
| 368 | * \param[in] element_size_in size in bytes of an element | ||
| 369 | * \param[in] dimension_in number of elements per item | ||
| 370 | */ | ||
| 371 | 146 | AttributeInfo( | |
| 372 | const std::string& name_in, | ||
| 373 | const std::string& element_type_in, | ||
| 374 | size_t element_size_in, | ||
| 375 | index_t dimension_in | ||
| 376 | 146 | ) : | |
| 377 | 146 | name(name_in), | |
| 378 |
1/2✓ Branch 1 taken 146 times.
✗ Branch 2 not taken.
|
146 | element_type(element_type_in), |
| 379 | 146 | element_size(element_size_in), | |
| 380 | 146 | dimension(dimension_in) { | |
| 381 | 146 | } | |
| 382 | |||
| 383 | |||
| 384 | /** | ||
| 385 | * \brief Name of the attribute. | ||
| 386 | */ | ||
| 387 | std::string name; | ||
| 388 | |||
| 389 | /** | ||
| 390 | * \brief A string with the name fo the C++ type | ||
| 391 | * of the elements. | ||
| 392 | */ | ||
| 393 | std::string element_type; | ||
| 394 | |||
| 395 | /** | ||
| 396 | * \brief The size in bytes of each element. | ||
| 397 | */ | ||
| 398 | size_t element_size; | ||
| 399 | |||
| 400 | /** | ||
| 401 | * \brief The number of elements per item. | ||
| 402 | */ | ||
| 403 | index_t dimension; | ||
| 404 | }; | ||
| 405 | |||
| 406 | /** | ||
| 407 | * \brief Internal representation of an attribute set. | ||
| 408 | */ | ||
| 409 | struct AttributeSetInfo { | ||
| 410 | |||
| 411 | /** | ||
| 412 | * \brief AttributeSetInfo constructor. | ||
| 413 | */ | ||
| 414 | 142 | AttributeSetInfo() : nb_items(0), skip(false) { | |
| 415 | 142 | } | |
| 416 | |||
| 417 | /** | ||
| 418 | * \brief AttributeSetInfo constructor. | ||
| 419 | * \param[in] name_in name of the attribute set | ||
| 420 | * \param[in] nb_items_in number of items in each attribute of the | ||
| 421 | * set | ||
| 422 | */ | ||
| 423 | 142 | AttributeSetInfo( | |
| 424 | const std::string& name_in, | ||
| 425 | index_t nb_items_in | ||
| 426 | 142 | ) : | |
| 427 | 142 | name(name_in), | |
| 428 | 142 | nb_items(nb_items_in), | |
| 429 | 142 | skip(false) { | |
| 430 | 142 | } | |
| 431 | |||
| 432 | /** | ||
| 433 | * \brief Finds an AttributeInfo by name. | ||
| 434 | * \param[in] name_in a const reference to the name of the | ||
| 435 | * attribute | ||
| 436 | * \return a const pointer to the AttributeInfo or nullptr if there | ||
| 437 | * is no such attribute. | ||
| 438 | */ | ||
| 439 | const AttributeInfo* find_attribute( | ||
| 440 | const std::string& name_in | ||
| 441 | ) const { | ||
| 442 | for(index_t i=0; i<attributes.size(); ++i) { | ||
| 443 | if(attributes[i].name == name_in) { | ||
| 444 | return &(attributes[i]); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | return nullptr; | ||
| 448 | } | ||
| 449 | |||
| 450 | /** | ||
| 451 | * \brief Finds an AttributeInfo by name. | ||
| 452 | * \param[in] name_in a const reference to the name of the | ||
| 453 | * attribute | ||
| 454 | * \return a pointer to the AttributeInfo or nullptr if there | ||
| 455 | * is no such attribute. | ||
| 456 | */ | ||
| 457 | 155 | AttributeInfo* find_attribute(const std::string& name_in) { | |
| 458 |
2/2✓ Branch 1 taken 59 times.
✓ Branch 2 taken 146 times.
|
205 | for(index_t i=0; i<attributes.size(); ++i) { |
| 459 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 3 taken 50 times.
|
59 | if(attributes[i].name == name_in) { |
| 460 | 9 | return &(attributes[i]); | |
| 461 | } | ||
| 462 | } | ||
| 463 | 146 | return nullptr; | |
| 464 | } | ||
| 465 | |||
| 466 | /** | ||
| 467 | * \brief name of the attribute set. | ||
| 468 | */ | ||
| 469 | std::string name; | ||
| 470 | |||
| 471 | /** | ||
| 472 | * \brief number of items in each attribute of the set. | ||
| 473 | */ | ||
| 474 | index_t nb_items; | ||
| 475 | |||
| 476 | /** | ||
| 477 | * \brief the attributes of the set. | ||
| 478 | */ | ||
| 479 | vector<AttributeInfo> attributes; | ||
| 480 | |||
| 481 | /** | ||
| 482 | * \brief if set, all attributes in the set are | ||
| 483 | * skipped when reading the file. | ||
| 484 | */ | ||
| 485 | bool skip; | ||
| 486 | }; | ||
| 487 | |||
| 488 | /** | ||
| 489 | * \brief Finds an attribute set by name. | ||
| 490 | * \param[in] name a const reference to the name of the attribute set | ||
| 491 | * \return a pointer to the AttributeSetInfo or nullptr if there is | ||
| 492 | * no such attribute set. | ||
| 493 | */ | ||
| 494 | 295 | AttributeSetInfo* find_attribute_set(const std::string& name) { | |
| 495 |
1/2✓ Branch 1 taken 295 times.
✗ Branch 2 not taken.
|
295 | auto it = attribute_sets_.find(name); |
| 496 |
2/2✓ Branch 2 taken 142 times.
✓ Branch 3 taken 153 times.
|
295 | if(it == attribute_sets_.end()) { |
| 497 | 142 | return nullptr; | |
| 498 | } | ||
| 499 | 153 | return &(it->second); | |
| 500 | } | ||
| 501 | |||
| 502 | /** | ||
| 503 | * \brief Finds an attribute set by name. | ||
| 504 | * \param[in] name a const reference to the name of the attribute set | ||
| 505 | * \return a const pointer to the AttributeSetInfo or nullptr if there is | ||
| 506 | * no such attribute set. | ||
| 507 | */ | ||
| 508 | const AttributeSetInfo* find_attribute_set( | ||
| 509 | const std::string& name | ||
| 510 | ) const { | ||
| 511 | auto it = attribute_sets_.find(name); | ||
| 512 | if(it == attribute_sets_.end()) { | ||
| 513 | return nullptr; | ||
| 514 | } | ||
| 515 | return &(it->second); | ||
| 516 | } | ||
| 517 | |||
| 518 | /** | ||
| 519 | * \brief Reads an integer from the file. | ||
| 520 | * \details Checks that I/O was completed and throws a | ||
| 521 | * GeoFileException if the file is truncated. Reads | ||
| 522 | * 32 or 64 bits depending on whether geogram file is | ||
| 523 | * in GARGANTUA mode, and truncates or expands depending | ||
| 524 | * on current GARGANTUA mode. If file is in GARGANTUA mode | ||
| 525 | * and not current mode, check whether read integer fits in | ||
| 526 | * 32 bits and throws an exception if it is not the case. | ||
| 527 | * \return the read integer | ||
| 528 | */ | ||
| 529 | index_t read_index_t(); | ||
| 530 | |||
| 531 | /** | ||
| 532 | * \brief Writes an integer into the file. | ||
| 533 | * \details Checks that I/O was completed and throws a | ||
| 534 | * GeoFileException if the file is truncated. In Standard | ||
| 535 | * mode a 32-bits integer is written. In Gargantua | ||
| 536 | * mode a 64-bits integer is written. | ||
| 537 | * \param[in] x the integer | ||
| 538 | * \param[in] comment an optional comment string, written to | ||
| 539 | * ASCII geofiles | ||
| 540 | */ | ||
| 541 | void write_index_t(index_t x, const char* comment = nullptr); | ||
| 542 | |||
| 543 | /** | ||
| 544 | * \brief Reads a 32-bit integer from the file. | ||
| 545 | * \details Checks that I/O was completed and throws a | ||
| 546 | * GeoFileException if the file is truncated. | ||
| 547 | * \return the read integer converted to index_t | ||
| 548 | */ | ||
| 549 | index_t read_index_t_32(); | ||
| 550 | |||
| 551 | /** | ||
| 552 | * \brief Writes a 32-bit integer into the file. | ||
| 553 | * \details Checks that I/O was completed and throws a | ||
| 554 | * GeoFileException if the file is truncated. | ||
| 555 | * \param[in] x the integer | ||
| 556 | * \param[in] comment an optional comment string, written to | ||
| 557 | * ASCII geofiles | ||
| 558 | */ | ||
| 559 | void write_index_t_32(index_t x, const char* comment = nullptr); | ||
| 560 | |||
| 561 | /** | ||
| 562 | * \brief Reads a string from the file. | ||
| 563 | * \details Checks that I/O was completed and throws a | ||
| 564 | * GeoFileException if the file is truncated. | ||
| 565 | * \return the read string | ||
| 566 | */ | ||
| 567 | std::string read_string(); | ||
| 568 | |||
| 569 | /** | ||
| 570 | * \brief Writes a string into the file. | ||
| 571 | * \details Checks that I/O was completed and throws a | ||
| 572 | * GeoFileException if the file is truncated. | ||
| 573 | * \param[in] s a const reference to the string | ||
| 574 | * \param[in] comment an optional comment string, written to | ||
| 575 | * ASCII geofiles | ||
| 576 | */ | ||
| 577 | void write_string(const std::string& s, const char* comment = nullptr); | ||
| 578 | |||
| 579 | /** | ||
| 580 | * \brief Reads an unsigned 64 bits integer from the file. | ||
| 581 | * \details Checks that I/O was completed and throws a | ||
| 582 | * GeoFileException if the file is truncated. | ||
| 583 | * \return the read integer | ||
| 584 | */ | ||
| 585 | size_t read_size_t(); | ||
| 586 | |||
| 587 | /** | ||
| 588 | * \brief Writes an unsigned 64 bits integer into the file. | ||
| 589 | * \details Checks that I/O was completed and throws a | ||
| 590 | * GeoFileException if the file is truncated. | ||
| 591 | * \param[in] x the integer | ||
| 592 | */ | ||
| 593 | void write_size_t(size_t x); | ||
| 594 | |||
| 595 | /** | ||
| 596 | * \brief Reads a chunk class from the file. | ||
| 597 | * \details A chunk class is a 4 characters string. | ||
| 598 | * The function checks that I/O was completed and throws a | ||
| 599 | * GeoFileException if the file is truncated. | ||
| 600 | * \return A 4 characters string with the chunk class. | ||
| 601 | */ | ||
| 602 | std::string read_chunk_class(); | ||
| 603 | |||
| 604 | /** | ||
| 605 | * \brief Writes a chunk class into the file. | ||
| 606 | * \details A chunk class is a 4 characters string. | ||
| 607 | * The function checks that I/O was completed and throws a | ||
| 608 | * GeoFileException if the file is truncated. | ||
| 609 | * \param[in] chunk_class A 4 characters string with the chunk class. | ||
| 610 | * \pre chunk_class.length() == 4 | ||
| 611 | */ | ||
| 612 | void write_chunk_class(const std::string& chunk_class); | ||
| 613 | |||
| 614 | /** | ||
| 615 | * \brief Writes a string array into the file. | ||
| 616 | * \param[in] strings the string array, as a const reference to | ||
| 617 | * a vector of strings. | ||
| 618 | */ | ||
| 619 | void write_string_array(const std::vector<std::string>& strings); | ||
| 620 | |||
| 621 | /** | ||
| 622 | * \brief Reads a string array from the file. | ||
| 623 | * \param[out] strings the read string array, as a reference to | ||
| 624 | * a vector of strings. | ||
| 625 | */ | ||
| 626 | void read_string_array(std::vector<std::string>& strings); | ||
| 627 | |||
| 628 | |||
| 629 | /** | ||
| 630 | * \brief Gets the size in bytes used by a given string in | ||
| 631 | * the file. | ||
| 632 | * \details The file stored the length of the string in a 32 | ||
| 633 | * bits integer plus all the characters of the string | ||
| 634 | * \return the size in bytes used to store the string in the | ||
| 635 | * file. | ||
| 636 | */ | ||
| 637 | 1975 | size_t string_size(const std::string& s) const { | |
| 638 | 1975 | return sizeof(Numeric::uint32) + s.length(); | |
| 639 | } | ||
| 640 | |||
| 641 | /** | ||
| 642 | * \brief Gets the size in bytes used by a given string array in | ||
| 643 | * the file. | ||
| 644 | * \return the size in bytes used to store the string array in the | ||
| 645 | * file. | ||
| 646 | */ | ||
| 647 | size_t string_array_size( | ||
| 648 | const std::vector<std::string>& strings | ||
| 649 | ) const ; | ||
| 650 | |||
| 651 | /** | ||
| 652 | * \brief Reads a chunk header from the file. | ||
| 653 | */ | ||
| 654 | void read_chunk_header(); | ||
| 655 | |||
| 656 | /** | ||
| 657 | * \brief Writes a chunk header into the file. | ||
| 658 | * \param[in] chunk_class the chunk class | ||
| 659 | * \param[in] size the size in bytes of the data | ||
| 660 | * attached to the chunk. | ||
| 661 | * \details When reading the file, to skip the chunk, | ||
| 662 | * one calls fseek(file_, size, SEEK_CUR) | ||
| 663 | */ | ||
| 664 | void write_chunk_header( | ||
| 665 | const std::string& chunk_class, size_t size | ||
| 666 | ); | ||
| 667 | |||
| 668 | /** | ||
| 669 | * \brief Checks that the actual chunk size corresponds | ||
| 670 | * to the specified chunk size. | ||
| 671 | */ | ||
| 672 | void check_chunk_size(); | ||
| 673 | |||
| 674 | /** | ||
| 675 | * \brief Compares the zlib version declared in the header | ||
| 676 | * file with the zlib version obtained from the runtime, | ||
| 677 | * and outputs an error message if they differ. | ||
| 678 | */ | ||
| 679 | void check_zlib_version(); | ||
| 680 | |||
| 681 | /** | ||
| 682 | * \brief Clears all memorized information about attributes | ||
| 683 | * and attribute sets. | ||
| 684 | * \details This function is called whenever a separator is read. | ||
| 685 | */ | ||
| 686 | void clear_attribute_maps(); | ||
| 687 | |||
| 688 | protected: | ||
| 689 | std::string filename_; | ||
| 690 | gzFile file_; | ||
| 691 | bool ascii_; | ||
| 692 | FILE* ascii_file_; | ||
| 693 | std::string current_chunk_class_; | ||
| 694 | size_t current_chunk_size_; | ||
| 695 | size_t current_chunk_file_pos_; | ||
| 696 | std::map<std::string, AttributeSetInfo> attribute_sets_; | ||
| 697 | |||
| 698 | static std::map<std::string, AsciiAttributeReadSerializer> | ||
| 699 | ascii_attribute_read_; | ||
| 700 | |||
| 701 | static std::map<std::string, AsciiAttributeWriteSerializer> | ||
| 702 | ascii_attribute_write_; | ||
| 703 | |||
| 704 | /** | ||
| 705 | * \brief True if current file is in GARGANTUA mode | ||
| 706 | * \details In GARGANTUA mode, index_t has 64 bits | ||
| 707 | */ | ||
| 708 | bool gargantua_mode_; | ||
| 709 | |||
| 710 | /** \brief True if reading a standard file in GARGANTUA mode */ | ||
| 711 | bool convert_32_to_64_; | ||
| 712 | |||
| 713 | /** \brief True if reading a GARGANTUA file in standard mode */ | ||
| 714 | bool convert_64_to_32_; | ||
| 715 | }; | ||
| 716 | |||
| 717 | /**************************************************************/ | ||
| 718 | |||
| 719 | /** | ||
| 720 | * \brief Used to read a structured binary file. | ||
| 721 | */ | ||
| 722 | class GEOGRAM_API InputGeoFile : public GeoFile { | ||
| 723 | public: | ||
| 724 | /** | ||
| 725 | * \brief InputGeoFile constructor. | ||
| 726 | * \param[in] filename a const reference to the file name. | ||
| 727 | */ | ||
| 728 | InputGeoFile(const std::string& filename); | ||
| 729 | |||
| 730 | /** | ||
| 731 | * \brief Advances to the next chunk. | ||
| 732 | * \return The read chunk class. | ||
| 733 | */ | ||
| 734 | const std::string& next_chunk(); | ||
| 735 | |||
| 736 | /** | ||
| 737 | * \brief Reads the latest attribute. | ||
| 738 | * \details This function can be only called right after next_chunk(), | ||
| 739 | * if it returned ATTRIBUTE. | ||
| 740 | */ | ||
| 741 | void read_attribute(void* addr); | ||
| 742 | |||
| 743 | |||
| 744 | /** | ||
| 745 | * \brief Indicates that all the attributes attached to the | ||
| 746 | * latest attribute set should be skipped. | ||
| 747 | * \details This function can be only called right after next_chunk(), | ||
| 748 | * if it returned ATTRIBUTE_SET. | ||
| 749 | */ | ||
| 750 | void skip_attribute_set(); | ||
| 751 | |||
| 752 | |||
| 753 | /** | ||
| 754 | * \brief Gets the current attribute set. | ||
| 755 | * \return a const reference to the AttributeSetInfo that | ||
| 756 | * represents the current attribute set | ||
| 757 | * \pre current chunk class is either "ATTR" (ATTRIBUTE) or | ||
| 758 | * ATTS (ATTRIBUTE_SET) | ||
| 759 | */ | ||
| 760 | 23 | const AttributeSetInfo& current_attribute_set() const { | |
| 761 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
23 | geo_assert(current_attribute_set_ != nullptr); |
| 762 | 23 | return *current_attribute_set_; | |
| 763 | } | ||
| 764 | |||
| 765 | /** | ||
| 766 | * \brief Gets the current attribute. | ||
| 767 | * \return a const reference to the AttributeInfo that | ||
| 768 | * represents the current attribute | ||
| 769 | * \pre current chunk class is "ATTR" (ATTRIBUTE) | ||
| 770 | */ | ||
| 771 | 29 | const AttributeInfo& current_attribute() const { | |
| 772 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
29 | geo_assert(current_attribute_ != nullptr); |
| 773 | 29 | return *current_attribute_; | |
| 774 | } | ||
| 775 | |||
| 776 | /* | ||
| 777 | * \brief Gets the current user comment. | ||
| 778 | * \return a const reference to the current user comment | ||
| 779 | * \pre current chunk class is "CMNT" (COMMENT) | ||
| 780 | */ | ||
| 781 | ✗ | const std::string& current_comment() const { | |
| 782 | ✗ | geo_assert(current_chunk_class_ == "CMNT"); | |
| 783 | ✗ | return current_comment_; | |
| 784 | } | ||
| 785 | |||
| 786 | |||
| 787 | /** | ||
| 788 | * \brief Reads the command line from the file. | ||
| 789 | * \details It is useful to save the command line arguments in | ||
| 790 | * each file, so that one can retrieve the parameters of each | ||
| 791 | * experiments when doing algorithm test. | ||
| 792 | * \param[out] args the command line, as a vector of strings | ||
| 793 | * \pre current_chunk_class() == "CMDL" | ||
| 794 | */ | ||
| 795 | void read_command_line(std::vector<std::string>& args); | ||
| 796 | |||
| 797 | protected: | ||
| 798 | /** | ||
| 799 | * \brief Skips the latest chunk. | ||
| 800 | * \details This function can only be called right | ||
| 801 | * after next_chunk(). | ||
| 802 | */ | ||
| 803 | void skip_chunk(); | ||
| 804 | |||
| 805 | /** | ||
| 806 | * \brief Reads an array of index_t, possibly with different | ||
| 807 | * GARGANTUA mode, and adapts it to current GARGANTUA mode. | ||
| 808 | * \param[out] addr buffer where to store the result | ||
| 809 | * \param[in] nb_elements number of elements to read | ||
| 810 | * \param[in] element_size size of an element (index_t), 4 or 8 | ||
| 811 | */ | ||
| 812 | void read_and_convert_index_t_array( | ||
| 813 | index_t* addr, size_t nb_elements, size_t element_size | ||
| 814 | ); | ||
| 815 | |||
| 816 | AttributeSetInfo* current_attribute_set_; | ||
| 817 | AttributeInfo* current_attribute_; | ||
| 818 | std::string current_comment_; | ||
| 819 | |||
| 820 | private: | ||
| 821 | /** | ||
| 822 | * \brief Forbids copy. | ||
| 823 | */ | ||
| 824 | InputGeoFile(const InputGeoFile& rhs); | ||
| 825 | |||
| 826 | /** | ||
| 827 | * \brief Forbids copy. | ||
| 828 | */ | ||
| 829 | InputGeoFile& operator=(const InputGeoFile& rhs); | ||
| 830 | }; | ||
| 831 | |||
| 832 | /**************************************************************/ | ||
| 833 | |||
| 834 | /** | ||
| 835 | * \brief Used to write a structured binary file. | ||
| 836 | */ | ||
| 837 | class GEOGRAM_API OutputGeoFile : public GeoFile { | ||
| 838 | public: | ||
| 839 | /** | ||
| 840 | * \brief OutputGeoFile constructor. | ||
| 841 | * \param[in] filename a const reference to the file name. | ||
| 842 | * \param[in] compression_level optional compression level, use | ||
| 843 | * 0 for uncompressed and 6 for maximum compression. | ||
| 844 | */ | ||
| 845 | OutputGeoFile(const std::string& filename, index_t compression_level=3); | ||
| 846 | |||
| 847 | /** | ||
| 848 | * \brief Writes a new attribute set to the file. | ||
| 849 | * \param[in] name a const reference to the name of | ||
| 850 | * the attribute set | ||
| 851 | * \param[in] nb_items number of items in the attribute set | ||
| 852 | */ | ||
| 853 | void write_attribute_set( | ||
| 854 | const std::string& name, index_t nb_items | ||
| 855 | ); | ||
| 856 | |||
| 857 | /** | ||
| 858 | * \brief Writes a new attribute to the file. | ||
| 859 | * \param[in] attribute_set_name a const reference to the name of | ||
| 860 | * an attribute set | ||
| 861 | * \param[in] attribute_name a const reference to the name of the | ||
| 862 | * attribute | ||
| 863 | * \param[in] element_type a const reference to the C++ name of the | ||
| 864 | * element type | ||
| 865 | * \param[in] element_size size in bytes of an element | ||
| 866 | * \param[in] dimension number of elements per item | ||
| 867 | * \param[in] data a const pointer to the data of the attribute, as | ||
| 868 | * a contiguous array of bytes in memory. | ||
| 869 | */ | ||
| 870 | void write_attribute( | ||
| 871 | const std::string& attribute_set_name, | ||
| 872 | const std::string& attribute_name, | ||
| 873 | const std::string& element_type, | ||
| 874 | size_t element_size, | ||
| 875 | index_t dimension, | ||
| 876 | const void* data | ||
| 877 | ); | ||
| 878 | |||
| 879 | |||
| 880 | /** | ||
| 881 | * \brief Writes a new comment to the file. | ||
| 882 | * \param[in] comment a const reference to the comment. | ||
| 883 | */ | ||
| 884 | void write_comment(const std::string& comment); | ||
| 885 | |||
| 886 | /** | ||
| 887 | * \brief Writes the command line to the file. | ||
| 888 | * \details It is useful to save the command line arguments in | ||
| 889 | * each file, so that one can retrieve the parameters of each | ||
| 890 | * experiments when doing algorithm test. | ||
| 891 | * \param[in] args the command line, as a vector of strings | ||
| 892 | */ | ||
| 893 | void write_command_line(const std::vector<std::string>& args); | ||
| 894 | |||
| 895 | |||
| 896 | /** | ||
| 897 | * \brief Writes a separator into the file. | ||
| 898 | * \details Separators are used to mark the boundaries between | ||
| 899 | * multiple objects saved in the same GeoFile. | ||
| 900 | */ | ||
| 901 | void write_separator(); | ||
| 902 | |||
| 903 | private: | ||
| 904 | /** | ||
| 905 | * \brief Forbids copy. | ||
| 906 | */ | ||
| 907 | OutputGeoFile(const InputGeoFile& rhs); | ||
| 908 | |||
| 909 | /** | ||
| 910 | * \brief Forbids copy. | ||
| 911 | */ | ||
| 912 | OutputGeoFile& operator=(const InputGeoFile& rhs); | ||
| 913 | }; | ||
| 914 | |||
| 915 | /**************************************************************/ | ||
| 916 | } | ||
| 917 | |||
| 918 | #endif | ||
| 919 |