GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/geofile.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 372 536 69.4%
Functions: 33 39 84.6%
Branches: 259 737 35.1%

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 #include <geogram/basic/geofile.h>
41 #include <geogram/basic/string.h>
42 #include <geogram/basic/logger.h>
43 #include <ctype.h>
44
45 #ifdef GEO_OS_WINDOWS
46 # define INT64_T_FMT "%Id"
47 #else
48 # include <inttypes.h>
49 # define INT64_T_FMT "%" PRIu64
50 #endif
51
52 #ifdef GARGANTUA
53 # define INDEX_T_FMT INT64_T_FMT
54 #else
55 # define INDEX_T_FMT "%u"
56 #endif
57
58 namespace {
59
60 void skip_comments(FILE* f) {
61
7/8
✓ Branch 1 taken 767 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 747 times.
✓ Branch 4 taken 20 times.
✓ Branch 6 taken 336 times.
✓ Branch 7 taken 8 times.
✓ Branch 9 taken 153 times.
✓ Branch 10 taken 3 times.
1267 while(char(fgetc(f)) != '\n');
62 }
63
64
65 108 std::string encode(const std::string& s) {
66 std::string result;
67
2/2
✓ Branch 0 taken 2062 times.
✓ Branch 1 taken 108 times.
4340 for(size_t i=0; i<s.size(); ++i) {
68
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 2062 times.
2062 switch(s[i]) {
69 case '\\':
70 result.push_back('\\');
71 result.push_back('\\');
72 break;
73 case '\"':
74 result.push_back('\\');
75 result.push_back('\"');
76 break;
77 default:
78
1/2
✓ Branch 1 taken 2062 times.
✗ Branch 2 not taken.
2062 result.push_back(s[i]);
79 }
80 }
81 108 return result;
82 }
83
84 20 std::string decode(const std::string& s) {
85 std::string result;
86 size_t i=0;
87
2/2
✓ Branch 0 taken 373 times.
✓ Branch 1 taken 20 times.
413 while(i < s.size()) {
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 373 times.
373 if(s[i] == '\\') {
89 ++i;
90 if(i >= s.size()) {
91 break;
92 }
93 }
94
1/2
✓ Branch 1 taken 373 times.
✗ Branch 2 not taken.
373 result.push_back(s[i]);
95 373 ++i;
96 }
97 20 return result;
98 }
99
100
101 /** \brief Maximum length of a gzlib gzread() or gzwrite() */
102 static constexpr GEO::Numeric::uint32 MAX_GZ_IO_SIZE = 1024*1024*1024;
103
104 #ifdef Z_LARGE64
105
106 #if defined(GEO_OS_WINDOWS)
107 typedef GEO::Numeric::int64 ssize_t;
108 #endif
109
110 /**
111 * \brief Wrapper around gzread() to read more than 4Gb
112 */
113 5 ssize_t gzread64(gzFile file, void* buf_in, size_t len) {
114 GEO::Memory::pointer buf = GEO::Memory::pointer(buf_in);
115 ssize_t bytes_read = 0;
116
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 while(len > size_t(MAX_GZ_IO_SIZE)) {
117 4 int result = gzread(file, buf, MAX_GZ_IO_SIZE);
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(result < 0) {
119 return ssize_t(result);
120 }
121 4 bytes_read += ssize_t(result);
122
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(result < int(MAX_GZ_IO_SIZE)) {
123 return ssize_t(bytes_read);
124 }
125 4 len -= MAX_GZ_IO_SIZE;
126 4 buf += MAX_GZ_IO_SIZE;
127 }
128 5 int result = gzread(file, buf, unsigned(len));
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(result < 0) {
130 return ssize_t(result);
131 }
132 5 bytes_read += ssize_t(result);
133 5 return ssize_t(bytes_read);
134 }
135
136 /**
137 * \brief Wrapper around gzwrite() to write more than 4Gb
138 */
139 133 ssize_t gzwrite64(gzFile file, const void* buf_in, size_t len) {
140 GEO::Memory::const_pointer buf = GEO::Memory::const_pointer(buf_in);
141 ssize_t bytes_read = 0;
142
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 133 times.
137 while(len > size_t(MAX_GZ_IO_SIZE)) {
143 4 int result = gzwrite(file, buf, MAX_GZ_IO_SIZE);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(result < 0) {
145 return ssize_t(result);
146 }
147 4 bytes_read += ssize_t(result);
148
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(result < int(MAX_GZ_IO_SIZE)) {
149 return ssize_t(bytes_read);
150 }
151 4 len -= MAX_GZ_IO_SIZE;
152 4 buf += MAX_GZ_IO_SIZE;
153 }
154 133 int result = gzwrite(file, buf, unsigned(len));
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
133 if(result < 0) {
156 return ssize_t(result);
157 }
158 133 bytes_read += ssize_t(result);
159 133 return ssize_t(bytes_read);
160 }
161
162 #else
163
164 // Normally it will not get there (64 bit file offsets are supported under
165 // Linux, Mac and Windows), but I keep the fallback here (for Android and
166 // Emscripten for instance).
167
168 #ifdef GEO_OS_WINDOWS
169 typedef GEO::Numeric::int64 ssize_t;
170 #endif
171
172 ssize_t gzread64(gzFile file, void* buf_in, size_t len) {
173 geo_assert(len < size_t(MAX_GZ_IO_SIZE));
174 return ssize_t(gzread(file, buf_in, unsigned(len)));
175 }
176
177 ssize_t gzwrite64(gzFile file, const void* buf_in, size_t len) {
178 geo_assert(len < size_t(MAX_GZ_IO_SIZE));
179 return ssize_t(gzwrite(file, buf_in, unsigned(len)));
180 }
181
182 ssize_t gztell64(gzFile file) {
183 return ssize_t(gztell(file));
184 }
185
186 ssize_t gzseek64(gzFile file, ssize_t off, int whence) {
187 geo_assert(
188 off >= -ssize_t(MAX_GZ_IO_SIZE) && off <= ssize_t(MAX_GZ_IO_SIZE)
189 );
190 return ssize_t(gzseek(file, z_off_t(off), whence));
191 }
192
193
194
195 #endif
196
197 }
198
199 namespace GEO {
200
201 /**************************************************************/
202
203 GeoFileException::~GeoFileException() GEO_NOEXCEPT {
204 }
205
206 /**************************************************************/
207
208 std::map<std::string, GeoFile::AsciiAttributeReadSerializer>
209 GeoFile::ascii_attribute_read_;
210
211 std::map<std::string, GeoFile::AsciiAttributeWriteSerializer>
212 GeoFile::ascii_attribute_write_;
213
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2763 times.
2763 void GeoFile::register_ascii_attribute_serializer(
215 const std::string& type_name,
216 AsciiAttributeReadSerializer read,
217 AsciiAttributeWriteSerializer write
218 ) {
219
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2763 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2763 geo_assert(
220 ascii_attribute_read_.find(type_name) ==
221 ascii_attribute_read_.end()
222 );
223
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2763 times.
2763 ascii_attribute_read_[type_name] = read;
224
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2763 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2763 geo_assert(
225 ascii_attribute_write_.find(type_name) ==
226 ascii_attribute_write_.end()
227 );
228 2763 ascii_attribute_write_[type_name] = write;
229 2763 }
230
231 /**************************************************************/
232
233 38 GeoFile::GeoFile(const std::string& filename) :
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 filename_(filename),
235 38 file_(nullptr),
236 38 ascii_(false),
237 38 ascii_file_(nullptr),
238
1/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
38 current_chunk_class_("0000"),
239 38 current_chunk_size_(0),
240
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 current_chunk_file_pos_(0) {
241
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
38 ascii_ = String::string_ends_with(filename, "_ascii");
242 38 gargantua_mode_ = false;
243 38 convert_32_to_64_ = false;
244 38 convert_64_to_32_ = false;
245 38 }
246
247 38 GeoFile::~GeoFile() {
248
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 4 times.
38 if(file_ != nullptr) {
249 34 gzclose(file_);
250 }
251
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 37 times.
38 if(ascii_file_ != nullptr) {
252 1 fclose(ascii_file_);
253 }
254 38 }
255
256 471 void GeoFile::check_chunk_size() {
257
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 452 times.
471 if(ascii_) {
258 19 return;
259 }
260 452 size_t chunk_size = size_t(gztell64(file_)) - current_chunk_file_pos_;
261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 452 times.
452 if(current_chunk_size_ != chunk_size) {
262 throw GeoFileException(
263 std::string("Chunk size mismatch: ") +
264 " expected " + String::to_string(current_chunk_size_) +
265 "/ got " + String::to_string(chunk_size)
266 );
267 }
268 }
269
270 36 void GeoFile::check_zlib_version() {
271
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
36 if(strcmp(ZLIB_VERSION, zlibVersion())) {
272 Logger::warn("GeoFile") << "ZLib version mismatch !" << std::endl;
273 Logger::warn("GeoFile") << " from header: " << ZLIB_VERSION
274 << std::endl;
275 Logger::warn("GeoFile") << " from runtime: " << zlibVersion()
276 << std::endl;
277 }
278 36 }
279
280 34 void GeoFile::read_chunk_header() {
281 34 current_chunk_class_ = read_chunk_class();
282
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 21 times.
34 if(ascii_) {
283
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12 times.
13 if(feof(ascii_file_)) {
284 1 fclose(ascii_file_);
285 1 ascii_file_ = nullptr;
286 1 current_chunk_size_ = 0;
287 current_chunk_class_ = "EOFL";
288 1 return;
289 }
290 } else {
291
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 19 times.
21 if(gzeof(file_)) {
292 2 gzclose(file_);
293 2 file_ = nullptr;
294 2 current_chunk_size_ = 0;
295 current_chunk_class_ = "EOFL";
296 2 return;
297 }
298
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 current_chunk_size_ = ascii_ ? 0 : read_size_t();
299
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 current_chunk_file_pos_ = ascii_ ? 0 : size_t(gztell64(file_));
300 }
301 }
302
303 447 void GeoFile::write_chunk_header(
304 const std::string& chunk_class, size_t size
305 ) {
306 447 write_chunk_class(chunk_class);
307
2/2
✓ Branch 0 taken 435 times.
✓ Branch 1 taken 12 times.
447 if(!ascii_) {
308 435 write_size_t(size);
309 }
310
2/2
✓ Branch 0 taken 435 times.
✓ Branch 1 taken 12 times.
447 current_chunk_file_pos_ = ascii_ ? 0 : size_t(gztell64(file_));
311 447 current_chunk_class_ = chunk_class;
312 447 current_chunk_size_ = size;
313 447 }
314
315 7 index_t GeoFile::read_index_t() {
316 7 index_t result=0;
317
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if(ascii_) {
318
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(fscanf(ascii_file_, INDEX_T_FMT, &result) == 0) {
319 throw GeoFileException("Could not read integer from file");
320 }
321 3 skip_comments(ascii_file_);
322 3 return result;
323 }
324
325
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(convert_32_to_64_) {
326 Numeric::uint32 result32 = 0;
327 int check = gzread(file_, &result32, sizeof(Numeric::uint32));
328 if(check == 0 && gzeof(file_)) {
329 result = NO_INDEX;
330 } else {
331 if(size_t(check) != sizeof(Numeric::uint32)) {
332 throw GeoFileException("Could not read integer from file");
333 }
334 result = index_t(result32);
335 }
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } else if(convert_64_to_32_) {
337 Numeric::uint64 result64 = 0;
338 int check = gzread(file_, &result64, sizeof(Numeric::uint64));
339 if(check == 0 && gzeof(file_)) {
340 result = NO_INDEX;
341 } else {
342 if(size_t(check) != sizeof(Numeric::uint64)) {
343 throw GeoFileException("Could not read integer from file");
344 }
345 if(result64 > std::numeric_limits<Numeric::uint32>::max()) {
346 throw GeoFileException(
347 "Integer in GARGANTUA file exceeds 32 bits"
348 );
349 }
350 result = index_t(result64);
351 }
352 } else {
353 4 int check = gzread(file_, &result, sizeof(index_t));
354
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
4 if(check == 0 && gzeof(file_)) {
355 result = NO_INDEX;
356 } else {
357
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(size_t(check) != sizeof(index_t)) {
358 throw GeoFileException("Could not read integer from file");
359 }
360 }
361 }
362 4 return result;
363 }
364
365 135 void GeoFile::write_index_t(index_t x, const char* comment) {
366
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 132 times.
135 if(ascii_) {
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(comment == nullptr) {
368 if(fprintf(ascii_file_,INDEX_T_FMT "\n",x) ==0) {
369 throw GeoFileException("Could not write integer to file");
370 }
371 } else {
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(
373 3 fprintf(
374 ascii_file_,INDEX_T_FMT "# this is %s\n",x,comment
375 ) ==0
376 ) {
377 throw GeoFileException("Could not write integer to file");
378 }
379 }
380 3 return;
381 }
382 132 int check = gzwrite(file_, &x, sizeof(index_t));
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132 if(size_t(check) != sizeof(index_t)) {
384 throw GeoFileException("Could not write integer to file");
385 }
386 }
387
388 /**********************************************************************/
389
390 47 index_t GeoFile::read_index_t_32() {
391 47 index_t result=0;
392
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 39 times.
47 if(ascii_) {
393
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(fscanf(ascii_file_, INDEX_T_FMT, &result) == 0) {
394 throw GeoFileException("Could not read integer from file");
395 }
396 8 skip_comments(ascii_file_);
397 #ifdef GARGANTUA
398 if(
399 result > index_t(std::numeric_limits<Numeric::uint32>::max())
400 ) {
401 throw GeoFileException(
402 "read_index_t_32(): integer does not fit in 32 bits"
403 );
404 }
405 #endif
406 8 return result;
407 }
408 39 Numeric::uint32 result32=0;
409 39 int check = gzread(file_, &result32, sizeof(Numeric::uint32));
410
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
39 if(check == 0 && gzeof(file_)) {
411 result = NO_INDEX;
412 } else {
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if(size_t(check) != sizeof(Numeric::uint32)) {
414 throw GeoFileException("Could not read integer from file");
415 }
416 }
417 39 result = index_t(result32);
418 39 return result;
419 }
420
421 2176 void GeoFile::write_index_t_32(index_t x, const char* comment) {
422 #ifdef GARGANTUA
423 if(x > index_t(std::numeric_limits<Numeric::uint32>::max())) {
424 throw GeoFileException(
425 "write_index_t_32(): integer does not fit in 32 bits"
426 );
427 }
428 #endif
429
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2167 times.
2176 if(ascii_) {
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(comment == nullptr) {
431 if(fprintf(ascii_file_,INDEX_T_FMT "\n",x) ==0) {
432 throw GeoFileException("Could not write integer to file");
433 }
434 } else {
435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(
436 9 fprintf(
437 ascii_file_,INDEX_T_FMT "# this is %s\n",x,comment
438 ) ==0
439 ) {
440 throw GeoFileException("Could not write integer to file");
441 }
442 }
443 9 return;
444 }
445 2167 Numeric::uint32 x32 = Numeric::uint32(x);
446 2167 int check = gzwrite(file_, &x32, sizeof(Numeric::uint32));
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2167 times.
2167 if(size_t(check) != sizeof(Numeric::uint32)) {
448 throw GeoFileException("Could not write integer to file");
449 }
450 }
451
452 /**********************************************************************/
453
454
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 29 times.
49 std::string GeoFile::read_string() {
455 std::string result;
456
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 29 times.
49 if(ascii_) {
457 int cur;
458
3/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 20 times.
31 while(char(cur = fgetc(ascii_file_)) != '\"') {
459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if(cur == EOF) {
460 throw GeoFileException(
461 "Could not read string data from file"
462 );
463 }
464 }
465
3/4
✓ Branch 1 taken 393 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 373 times.
✓ Branch 4 taken 20 times.
393 while(char(cur = fgetc(ascii_file_)) != '\"') {
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 373 times.
373 if(cur == EOF) {
467 throw GeoFileException(
468 "Could not read string data from file"
469 );
470 }
471
1/2
✓ Branch 1 taken 373 times.
✗ Branch 2 not taken.
373 result.push_back(char(cur));
472 }
473 20 skip_comments(ascii_file_);
474
1/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
20 result = decode(result);
475 20 return result;
476 }
477
478
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 index_t len=read_index_t_32();
479
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 result.resize(len);
480
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 if(len != 0) {
481
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 int check = gzread(file_, &result[0], (unsigned int)(len));
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if(index_t(check) != len) {
483 throw GeoFileException("Could not read string data from file");
484 }
485 }
486 return result;
487 }
488
489 1975 void GeoFile::write_string(const std::string& str, const char* comment) {
490
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 1867 times.
1975 if(ascii_) {
491
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 15 times.
108 if(comment == nullptr) {
492
2/4
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 93 times.
186 if(fprintf(ascii_file_, "\"%s\"\n", encode(str).c_str()) == 0) {
493 throw GeoFileException(
494 "Could not write string data to file"
495 );
496 }
497 } else {
498
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 if(fprintf(
499 ascii_file_, "\"%s\" # this is %s\n",
500
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
30 encode(str).c_str(), comment) == 0
501 ) {
502 throw GeoFileException(
503 "Could not write string data to file"
504 );
505 }
506 }
507 108 return;
508 }
509 1867 index_t len = index_t(str.length());
510 1867 write_index_t_32(len);
511
1/2
✓ Branch 0 taken 1867 times.
✗ Branch 1 not taken.
1867 if(len != 0) {
512 1867 int check = gzwrite(file_, &str[0], (unsigned int)(len));
513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1867 times.
1867 if(index_t(check) != len) {
514 throw GeoFileException("Could not write string data to file");
515 }
516 }
517 }
518
519 19 size_t GeoFile::read_size_t() {
520 19 Numeric::uint64 result=0;
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(ascii_) {
522 if(fscanf(ascii_file_, INT64_T_FMT "\n", &result) == 0) {
523 throw GeoFileException("Could not write size to file");
524 }
525 } else {
526 19 int check = gzread(file_, &result, sizeof(Numeric::uint64));
527
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
19 if(check == 0 && gzeof(file_)) {
528 result = size_t(-1);
529 } else {
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(size_t(check) != sizeof(Numeric::uint64)) {
531 throw GeoFileException("Could not read size from file");
532 }
533 }
534 }
535 19 return size_t(result);
536 }
537
538 435 void GeoFile::write_size_t(size_t x_in) {
539 435 Numeric::uint64 x = Numeric::uint64(x_in);
540
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 435 times.
435 if(ascii_) {
541 if(fprintf(ascii_file_, INT64_T_FMT "\n", x) == 0) {
542 throw GeoFileException("Could not write size to file");
543 }
544 } else {
545 435 int check = gzwrite(file_, &x, sizeof(Numeric::uint64));
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 435 times.
435 if(size_t(check) != sizeof(Numeric::uint64)) {
547 throw GeoFileException("Could not write size to file");
548 }
549 }
550 435 }
551
552
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 21 times.
34 std::string GeoFile::read_chunk_class() {
553 std::string result;
554
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 21 times.
34 if(ascii_) {
555 int cur;
556
3/4
✓ Branch 1 taken 2001 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1989 times.
✓ Branch 4 taken 12 times.
2001 while(char(cur = fgetc(ascii_file_)) != '[') {
557
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1988 times.
1989 if(cur == EOF) {
558 result = "EOFL";
559 return result;
560 }
561 }
562
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12 times.
60 for(index_t i=0; i<4; ++i) {
563
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 cur = fgetc(ascii_file_);
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(cur == EOF) {
565 throw GeoFileException(
566 "Could not read chunk class from file"
567 );
568 }
569
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 result.push_back(char(cur));
570 }
571
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 cur = fgetc(ascii_file_);
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(char(cur) != ']') {
573 throw GeoFileException(
574 "Could not read chunk class from file"
575 );
576 }
577 return result;
578 }
579
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 result.resize(4,'\0');
580
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 int check = gzread(file_, &result[0], 4);
581
4/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
21 if(check == 0 && gzeof(file_)) {
582 result = "EOFL";
583 } else {
584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(check != 4) {
585 throw GeoFileException("Could not read chunk class from file");
586 }
587 }
588 return result;
589 }
590
591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 447 times.
447 void GeoFile::write_chunk_class(const std::string& chunk_class) {
592
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 447 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
447 geo_assert(chunk_class.length() == 4);
593
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 435 times.
447 if(ascii_) {
594
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if(fprintf(ascii_file_, "[%s]\n", chunk_class.c_str()) == 0) {
595 throw GeoFileException("Could not write chunk class to file");
596 }
597 return;
598 }
599 435 int check = gzwrite(file_, &chunk_class[0], 4);
600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 435 times.
435 if(check != 4) {
601 throw GeoFileException("Could not write chunk class to file");
602 }
603 }
604
605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 void GeoFile::write_string_array(const std::vector<std::string>& strings) {
606 35 write_index_t_32(index_t(strings.size()),"the number of strings");
607
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1289 times.
✓ Branch 2 taken 1254 times.
✓ Branch 3 taken 35 times.
1289 for(index_t i=0; i<strings.size(); ++i) {
608 1254 write_string(strings[i]);
609 }
610 35 }
611
612 void GeoFile::read_string_array(std::vector<std::string>& strings) {
613 index_t nb_strings = read_index_t_32();
614 strings.resize(nb_strings);
615 for(index_t i=0; i<nb_strings; ++i) {
616 strings[i] = read_string();
617 }
618 }
619
620 35 size_t GeoFile::string_array_size(
621 const std::vector<std::string>& strings
622 ) const {
623 size_t result = sizeof(Numeric::uint32);
624
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1289 times.
✓ Branch 2 taken 1254 times.
✓ Branch 3 taken 35 times.
1289 for(index_t i=0; i<strings.size(); ++i) {
625 1254 result += string_size(strings[i]);
626 }
627 35 return result;
628 }
629
630 void GeoFile::clear_attribute_maps() {
631 attribute_sets_.clear();
632 }
633
634 /**********************************************************************/
635
636 3 InputGeoFile::InputGeoFile(
637 const std::string& filename
638 3 ) : GeoFile(filename),
639 3 current_attribute_set_(nullptr),
640
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 current_attribute_(nullptr)
641 {
642
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if(ascii_) {
643
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ascii_file_ = fopen(filename.c_str(), "rb");
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(ascii_file_ == nullptr) {
645 throw GeoFileException("Could not open file: " + filename);
646 }
647 } else {
648
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 check_zlib_version();
649
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 file_ = gzopen(filename.c_str(), "rb");
650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(file_ == nullptr) {
651 throw GeoFileException("Could not open file: " + filename);
652 }
653 }
654
655
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 read_chunk_header();
656
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(current_chunk_class_ != "HEAD") {
657 throw GeoFileException(
658 filename + " Does not start with HEAD chunk"
659 );
660 }
661
662
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::string magic = read_string();
663
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if(magic != "GEOGRAM" && magic != "GEOGRAM-GARGANTUA") {
664 throw GeoFileException(
665 filename + " is not a GEOGRAM file" +
666 " (got magic=" + magic + ")"
667 );
668 }
669
670 3 gargantua_mode_ = (magic == "GEOGRAM-GARGANTUA");
671
672 #ifdef GARGANTUA
673 convert_32_to_64_ = !gargantua_mode_;
674 convert_64_to_32_ = false;
675 #else
676 3 convert_64_to_32_ = gargantua_mode_;
677 3 convert_32_to_64_ = false;
678 #endif
679
680
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::string version = read_string();
681 geo_argused(version);
682 // Logger::out("I/O") << "GeoFile version: " << version << std::endl;
683
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 check_chunk_size();
684 3 }
685
686 31 const std::string& InputGeoFile::next_chunk() {
687
688 // If the file pointer did not advance as expected
689 // between two consecutive calls of next_chunk, it
690 // means that the client code does not want to
691 // read the current chunk, then it needs to be
692 // skipped.
693
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 12 times.
31 if(ascii_) {
694 // TODO: skip chunk mechanism for ASCII
695 } else {
696 19 if(
697 19 size_t(gztell64(file_)) !=
698
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
19 current_chunk_file_pos_ + current_chunk_size_
699 ) {
700 2 skip_chunk();
701 }
702 }
703
704 31 read_chunk_header();
705
706
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
31 if(current_chunk_class_ == "ATTS") {
707 7 std::string attribute_set_name = read_string();
708
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 index_t nb_items = read_index_t();
709
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 check_chunk_size();
710
711 if(find_attribute_set(attribute_set_name) != nullptr) {
712 throw GeoFileException(
713 "Duplicate attribute set " + attribute_set_name
714 );
715 }
716
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 attribute_sets_[attribute_set_name] =
717
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 AttributeSetInfo(attribute_set_name, nb_items);
718 7 current_attribute_set_ = find_attribute_set(attribute_set_name);
719
1/10
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
7 geo_assert(current_attribute_set_ != nullptr);
720 7 current_attribute_ = nullptr;
721
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 current_comment_ = "";
722
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 15 times.
24 } else if(current_chunk_class_ == "ATTR") {
723 9 std::string attribute_set_name = read_string();
724
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 std::string attribute_name = read_string();
725
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 std::string element_type = read_string();
726
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 index_t element_size = read_index_t_32();
727
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 index_t dimension = read_index_t_32();
728 9 current_attribute_set_ = find_attribute_set(attribute_set_name);
729 9 if(
730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 current_attribute_set_->find_attribute(attribute_name)
731 != nullptr
732 ) {
733 throw GeoFileException(
734 "Duplicate attribute " + attribute_name +
735 " in attribute set " + attribute_set_name
736 );
737 }
738 9 current_attribute_set_->attributes.push_back(
739 9 AttributeInfo(
740 attribute_name,
741 element_type,
742 element_size,
743 dimension
744
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 )
745 );
746 9 current_attribute_ =
747 9 current_attribute_set_->find_attribute(attribute_name);
748
1/10
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
9 geo_assert(current_attribute_ != nullptr);
749
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 current_comment_ = "";
750
751
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(current_attribute_set_->skip) {
752 skip_chunk();
753 return next_chunk();
754 }
755
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
15 } else if(current_chunk_class_ == "CMNT") {
756 9 current_attribute_ = nullptr;
757 9 current_attribute_set_ = nullptr;
758 9 current_comment_ = read_string();
759 9 check_chunk_size();
760
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 } else if(current_chunk_class_ == "SPTR") {
761 clear_attribute_maps();
762 }
763 return current_chunk_class_;
764 }
765
766 9 void InputGeoFile::read_attribute(void* addr) {
767
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
9 geo_assert(current_chunk_class_ == "ATTR");
768
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 if(ascii_) {
769 AsciiAttributeReadSerializer read_attribute_func =
770 4 ascii_attribute_read_[current_attribute_->element_type];
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(read_attribute_func == nullptr) {
772 throw GeoFileException(
773 "No ASCII serializer for type:" +
774 current_attribute_->element_type
775 );
776 }
777 4 bool result = (*read_attribute_func)(
778 ascii_file_,
779 Memory::pointer(addr),
780 index_t(
781 4 current_attribute_set_->nb_items*
782 4 current_attribute_->dimension
783 )
784 );
785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(!result) {
786 throw GeoFileException(
787 "Could not read attribute " + current_attribute_->name +
788 " in set " + current_attribute_set_->name
789 );
790 }
791 return;
792 }
793
794
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(current_attribute_->element_type == "index_t") {
795 2 read_and_convert_index_t_array(
796 reinterpret_cast<index_t*>(addr),
797 2 size_t(current_attribute_->dimension) *
798 2 size_t(current_attribute_set_->nb_items),
799 2 size_t(current_attribute_->element_size)
800 );
801 } else {
802 3 size_t size =
803 3 size_t(current_attribute_->element_size) *
804 3 size_t(current_attribute_->dimension) *
805 3 size_t(current_attribute_set_->nb_items);
806 3 ssize_t check = gzread64(file_, addr, size);
807
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(size_t(check) != size) {
808 throw GeoFileException(
809 "Could not read attribute " + current_attribute_->name +
810 " in set " + current_attribute_set_->name +
811 " (" + String::to_string(check) + "/"
812 + String::to_string(size) + " bytes read)"
813 );
814 }
815 }
816 5 check_chunk_size();
817 }
818
819 2 void InputGeoFile::read_and_convert_index_t_array(
820 index_t* addr, size_t nb_elements, size_t element_size
821 ) {
822
823 #ifdef __clang__
824 #pragma GCC diagnostic push
825 #pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"
826 #endif
827
828 // Three modes:
829 // copy (just read index_t array)
830 // expand (read 32 bits, store 64 bits). Beware NO_INDEX
831 // truncate (read 64 bits, store 32 bits). Beware NO_INDEX, chk bounds
832 bool copy = (element_size == sizeof(index_t));
833 bool expand = (element_size == 4 && sizeof(index_t) == 8);
834 bool truncate = (element_size == 8 && sizeof(index_t) == 4);
835
836 static constexpr Numeric::uint32 NO_INDEX32 = Numeric::uint32(-1);
837 static constexpr Numeric::uint64 NO_INDEX64 = Numeric::uint64(-1);
838
839 Numeric::uint32* addr32 = reinterpret_cast<Numeric::uint32*>(addr);
840
841
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(copy || expand) {
842 2 size_t size = nb_elements * element_size;
843 2 ssize_t check = gzread64(file_, addr, size);
844
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(size_t(check) != size) {
845 throw GeoFileException(
846 "Could not read attribute " + current_attribute_->name +
847 " in set " + current_attribute_set_->name +
848 " (" + String::to_string(check) + "/"
849 + String::to_string(size) + " bytes read)"
850 );
851 }
852 if(expand) {
853 // Convert values starting from end of array so that we do
854 // not overwrite values before using them !
855 for(ssize_t i = ssize_t(nb_elements-1); i>=0; --i) {
856 addr[i] = (addr32[i] == NO_INDEX32)
857 ? NO_INDEX : index_t(addr32[i]) ;
858 }
859 }
860 } else if(truncate) {
861
862 // Use a temporary buffer of max 1M entries. 64-bit indices are
863 // read there, then each chunk of 1M entries is converted and
864 // written to the destination array (addr).
865
866 static constexpr size_t BUFFER_SIZE = 1024*1024;
867 size_t buffer_size = std::min(nb_elements, BUFFER_SIZE);
868
869 index_t* to_addr = addr;
870 Numeric::uint64* buff64 = new Numeric::uint64[buffer_size];
871 size_t nb_elements_to_read = nb_elements;
872
873 while(nb_elements_to_read != 0) {
874 size_t nb_elements_in_buffer = std::min(
875 nb_elements_to_read, BUFFER_SIZE
876 );
877
878 size_t size_to_read =
879 nb_elements_in_buffer * sizeof(Numeric::uint64);
880
881 ssize_t check = gzread64(file_, buff64, size_to_read);
882
883 if(size_t(check) != size_to_read) {
884 throw GeoFileException(
885 "Could not read attribute " + current_attribute_->name +
886 " in set " + current_attribute_set_->name +
887 " ( buffread" + String::to_string(check) + "/"
888 + String::to_string(size_to_read) + " bytes read)"
889 );
890 }
891
892 for(size_t i=0; i<nb_elements_in_buffer; ++i) {
893 index_t val = 0;
894 if(buff64[i] == NO_INDEX64) {
895 val = NO_INDEX;
896 } else if(buff64[i] > std::numeric_limits<index_t>::max()) {
897 throw GeoFileException(
898 "Could not read attribute " + current_attribute_->name +
899 " in set " + current_attribute_set_->name +
900 " ( index_t exceeds max 32-bit index )"
901 );
902 } else {
903 val = index_t(buff64[i]);
904 }
905 to_addr[i] = val;
906 }
907
908 to_addr += nb_elements_in_buffer;
909 nb_elements_to_read -= nb_elements_in_buffer;
910 }
911 delete[] buff64;
912 } else {
913 geo_assert_not_reached;
914 }
915
916 #ifdef __clang__
917 #pragma clang diagnostic pop
918 #endif
919 2 }
920
921
922 2 void InputGeoFile::skip_chunk() {
923
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(ascii_) {
924 // TODO
925 return;
926 }
927 2 gzseek64(
928 file_,
929 2 ssize_t(current_chunk_size_ + current_chunk_file_pos_),
930 SEEK_SET
931 );
932 }
933
934 void InputGeoFile::skip_attribute_set() {
935 geo_assert(current_chunk_class_ == "ATTS");
936 current_attribute_set_->skip = true;
937 }
938
939 void InputGeoFile::read_command_line(
940 std::vector<std::string>& args
941 ) {
942 geo_assert(current_chunk_class() == "CMDL");
943 read_string_array(args);
944 check_chunk_size();
945 }
946
947 /**************************************************************/
948
949 35 OutputGeoFile::OutputGeoFile(
950 const std::string& filename, index_t compression_level
951 35 ) : GeoFile(filename) {
952
953 #ifdef GARGANTUA
954 gargantua_mode_ = true;
955 #endif
956
957
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 34 times.
35 if(ascii_) {
958
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ascii_file_ = fopen(filename.c_str(), "wb");
959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(ascii_file_ == nullptr) {
960 throw GeoFileException("Could not create file: " + filename);
961 }
962 } else {
963
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
34 check_zlib_version();
964
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if(compression_level == 0) {
965 file_ = gzopen(filename.c_str(), "wb");
966 } else {
967
2/4
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
✗ Branch 4 not taken.
34 file_ = gzopen(
968 filename.c_str(),
969
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
68 ("wb" + String::to_string(compression_level)).c_str()
970 );
971 }
972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if(file_ == nullptr) {
973 throw GeoFileException("Could not create file: " + filename);
974 }
975 }
976
977 #ifdef GARGANTUA
978 std::string magic = "GEOGRAM-GARGANTUA";
979 #else
980
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 std::string magic = "GEOGRAM";
981 #endif
982
2/6
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
35 std::string version = "1.0";
983
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
35 write_chunk_header("HEAD", string_size(magic) + string_size(version));
984
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 write_string(magic);
985
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 write_string(version);
986
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 check_chunk_size();
987
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 write_comment(
988
3/6
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 35 times.
✗ Branch 8 not taken.
35 "geogram version=" + Environment::instance()->get_value("version")
989 );
990
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 write_comment(
991 35 "geogram release date=" +
992
3/6
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 35 times.
✗ Branch 8 not taken.
35 Environment::instance()->get_value("release_date")
993 );
994
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 write_comment(
995 35 "geogram SVN revision=" +
996
4/10
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 35 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 35 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
70 Environment::instance()->get_value("SVN revision")
997 );
998 35 }
999
1000
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
135 void OutputGeoFile::write_attribute_set(
1001 const std::string& attribute_set_name, index_t nb_items
1002 ) {
1003 geo_assert(find_attribute_set(attribute_set_name) == nullptr);
1004
1005
1/2
✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
135 attribute_sets_[attribute_set_name] =
1006 135 AttributeSetInfo(attribute_set_name, nb_items);
1007
1008
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
✓ Branch 3 taken 135 times.
✗ Branch 4 not taken.
270 write_chunk_header(
1009 135 "ATTS",
1010 string_size(attribute_set_name) +
1011 sizeof(index_t)
1012 );
1013
1014 135 write_string(attribute_set_name, "the name of this attribute set");
1015 135 write_index_t(nb_items, "the number of items in this attribute set");
1016
1017 135 check_chunk_size();
1018 135 }
1019
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 void OutputGeoFile::write_attribute(
1021 const std::string& attribute_set_name,
1022 const std::string& attribute_name,
1023 const std::string& element_type,
1024 size_t element_size,
1025 index_t dimension,
1026 const void* data
1027 ) {
1028 AttributeSetInfo* attribute_set_info = find_attribute_set(
1029 attribute_set_name
1030 );
1031 geo_assert(attribute_set_info != nullptr);
1032
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
137 geo_assert(
1033 attribute_set_info->find_attribute(attribute_name) == nullptr
1034 );
1035
1036 size_t data_size =
1037 137 element_size * dimension *
1038 137 attribute_sets_[attribute_set_name].nb_items;
1039
1040
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
✓ Branch 3 taken 137 times.
✗ Branch 4 not taken.
274 write_chunk_header(
1041 274 "ATTR",
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 string_size(attribute_set_name) +
1043 137 string_size(attribute_name) +
1044 string_size(element_type) +
1045 sizeof(Numeric::uint32) +
1046 137 sizeof(Numeric::uint32) +
1047 data_size
1048 );
1049
1050 137 write_string(
1051 attribute_set_name,
1052 "the name of the attribute set this attribute belongs to"
1053 );
1054 137 write_string(attribute_name, "the name of this attribute");
1055 137 write_string(
1056 element_type, "the type of the elements in this attribute"
1057 );
1058 137 write_index_t_32(
1059 index_t(element_size), "the size of an element (in bytes)"
1060 );
1061 137 write_index_t_32(dimension, "the number of elements per item");
1062
1063
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 133 times.
137 if(ascii_) {
1064 AsciiAttributeWriteSerializer write_attribute_func =
1065 4 ascii_attribute_write_[element_type];
1066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(write_attribute_func == nullptr) {
1067 throw GeoFileException(
1068 "No ASCII serializer for type:"+element_type
1069 );
1070 }
1071 4 bool result = (*write_attribute_func)(
1072 ascii_file_, Memory::const_pointer(data),
1073 4 index_t(data_size/element_size)
1074 );
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(!result) {
1076 throw GeoFileException("Could not write attribute data");
1077 }
1078 } else {
1079 133 ssize_t check = gzwrite64(file_, data, data_size);
1080
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
133 if(size_t(check) != data_size) {
1081 throw GeoFileException("Could not write attribute data");
1082 }
1083 }
1084
1085 137 check_chunk_size();
1086
1087 137 attribute_set_info->attributes.push_back(
1088 274 AttributeInfo(attribute_name, element_type, element_size, dimension)
1089 );
1090 137 }
1091
1092 105 void OutputGeoFile::write_comment(const std::string& comment) {
1093
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
✓ Branch 3 taken 105 times.
✗ Branch 4 not taken.
210 write_chunk_header(
1094 105 "CMNT",
1095 string_size(comment)
1096 );
1097 105 write_string(comment);
1098 105 check_chunk_size();
1099 105 }
1100
1101 35 void OutputGeoFile::write_command_line(
1102 const std::vector<std::string>& args
1103 ) {
1104
1/2
✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
35 write_chunk_header("CMDL", string_array_size(args));
1105
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 34 times.
35 if(ascii_) {
1106 std::vector<std::string> new_args;
1107
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 1 times.
89 for(const std::string& arg : args) {
1108 bool serializable = true;
1109
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1777 times.
✓ Branch 2 taken 1689 times.
✓ Branch 3 taken 88 times.
1777 for(index_t i=0; i<arg.size(); ++i) {
1110
2/4
✓ Branch 0 taken 1689 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1689 times.
✗ Branch 3 not taken.
1689 if(!isprint(arg[i]) || arg[i] == '\"') {
1111 serializable = false;
1112 break;
1113 }
1114 }
1115
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if(serializable) {
1116
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 new_args.push_back(arg);
1117 } else {
1118 Logger::warn("GeoFile") << "Skipping arg: "
1119 << arg << std::endl;
1120 }
1121 }
1122
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 write_string_array(new_args);
1123 1 } else {
1124 34 write_string_array(args);
1125 }
1126 35 check_chunk_size();
1127 35 }
1128
1129 void OutputGeoFile::write_separator() {
1130 clear_attribute_maps();
1131 write_chunk_header("SPTR", 4);
1132 write_chunk_class("____");
1133 check_chunk_size();
1134 }
1135
1136 /**************************************************************/
1137 }
1138