GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/b_stream.h
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 4 9 44.4%
Functions: 0 0 -%
Branches: 7 28 25.0%

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_B_STREAM
41 #define GEOGRAM_BASIC_B_STREAM
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/numeric.h>
45
46 #include <iostream>
47 #include <fstream>
48 #include <string>
49
50 /**
51 * \file geogram/basic/b_stream.h
52 * \brief Provides classes for reading and writing binary data
53 */
54
55 namespace GEO {
56
57 /**
58 * \brief Binary stream base class
59 * \details
60 * A characteristic of processors named endian corresponds to the
61 * way they store numbers. A little endian processor stores
62 * less significant bytes first, and a big endian processor
63 * stores most significant bytes first.
64 *
65 * Most Unix machines are big endian, except those having
66 * a DEC Alpha processor. Intel processors are little endian.
67 * This causes portability problems for binary files to be
68 * exchanged between machines of the two types. The classes
69 * BinaryInputStream and BinaryOutputStream make it possible
70 * to deal with this problem.
71 *
72 * We have not used XDR because XDR assumes that the external
73 * format is always big endian, which would have prevented us
74 * to read files from PC softwares (such as 3DS binary files).
75 */
76 class GEOGRAM_API BinaryStream {
77 public:
78 /** Constant to use to specify little-endian streams */
79 static const int GEO_LITTLE_ENDIAN = 0;
80
81 /** Constant to use to specify big-endian streams */
82 static const int GEO_BIG_ENDIAN = 1;
83
84 /**
85 * \brief Sets the stream endianness
86 * \details This affects how the integer and floating point values
87 * will be stored in the stream.
88 * \param[in] stream_endian the endianness of the stream:
89 * - GEO_LITTLE_ENDIAN makes the stream little-endian
90 * - GEO_BIG_ENDIAN makes the stream big-endian
91 */
92 void set_stream_endian(int stream_endian);
93
94 /**
95 * \brief Gets the stream endianness
96 * \return \c GEO_LITTLE_ENDIAN if the stream is little-endian,
97 * \c GEO_BIG_ENDIAN if the stream is big-endian.
98 */
99 inline int stream_endian() const {
100 return stream_endian_;
101 }
102
103 /**
104 * \brief Gets the current architecture's endianness
105 * \return \c GEO_LITTLE_ENDIAN if the architecture is little-endian,
106 * \c GEO_BIG_ENDIAN if the architecture is big-endian.
107 */
108 inline int machine_endian() const {
109 return machine_endian_;
110 }
111
112 /**
113 * \brief Checks support for record markers
114 * \see set_has_record_markers().
115 */
116 inline bool has_record_markers() const {
117 return has_record_markers_;
118 }
119
120 /**
121 * \brief Enables/disables support for record markers
122 * \details
123 * Some FORTRAN files have their records surrounded by two
124 * integers and some not. This function enables BinaryStream
125 * to read such FORTRAN files. Default value is true.
126 */
127 inline void set_has_record_markers(bool b) {
128 has_record_markers_ = b;
129 }
130
131 protected:
132 /**
133 * \brief Base constructor
134 * \details This constructor initializes the base class common to
135 * BinaryInputStream and BinaryOutputStream. It sets the stream
136 * endianness and detects the current architecture's endianness
137 * \param[in] stream_endian the endianness of the stream:
138 * - GEO_LITTLE_ENDIAN makes the stream little-endian
139 * - GEO_BIG_ENDIAN makes the stream big-endian (the default)
140 */
141 BinaryStream(int stream_endian = GEO_BIG_ENDIAN);
142
143 /**
144 * \brief Detects the current architecture's endianness.
145 */
146 void detect_machine_endian();
147
148 /**
149 * \brief Size selector
150 * \details This is used to overload low-level read() and write()
151 * functions according to the size of the elements to read/write
152 */
153 template <size_t N>
154 struct ItemSize {
155 };
156
157 protected:
158 /** True if stream needs swapping */
159 bool swapped_;
160
161 private:
162 int machine_endian_;
163 int stream_endian_;
164 bool has_record_markers_;
165 };
166
167 /************************************************************************/
168
169 /**
170 * \brief Binary input file
171 * \details
172 * This class enables binary files to be read, while taking
173 * into account Endian problems (see BinaryStream).
174 */
175 class GEOGRAM_API BinaryInputStream : public BinaryStream {
176 public:
177 /**
178 * \brief Creates a new binary input stream
179 * \details This prepares the stream to read data from file \p
180 * file_name. Data is supposed to be stored with the given endianness
181 * \p stream_endian.
182 * \param[in] file_name path to the file to read
183 * \param[in] stream_endian the endianness of the stream:
184 * - GEO_LITTLE_ENDIAN makes the stream little-endian
185 * - GEO_BIG_ENDIAN makes the stream big-endian (the default)
186 */
187 BinaryInputStream(
188 const std::string& file_name, int stream_endian = GEO_BIG_ENDIAN
189 );
190
191 /**
192 * \brief Creates a new binary input stream
193 * \details This prepares the BinaryInputStream to read data from
194 * std::istream \p input. Data will be stored with the given endianness
195 * \p stream_endian.
196 * \param[in] input the input stream to read from
197 * \param[in] stream_endian the endianness of the stream:
198 * - GEO_LITTLE_ENDIAN makes the stream little-endian
199 * - GEO_BIG_ENDIAN makes the stream big-endian (the default)
200 */
201 BinaryInputStream(
202 std::istream& input, int stream_endian = GEO_BIG_ENDIAN
203 );
204
205 /**
206 * \brief Deletes the input stream
207 * \details This closes any associated std::istream and closes any
208 * associated file.
209 */
210 ~BinaryInputStream();
211
212 /**
213 * \brief Gets the status of the stream
214 * \retval true if the stream is valid
215 * \retval false if an error occurred
216 */
217 bool OK() const;
218
219 /**
220 * \brief Checks if there are more bytes to read
221 * \retval true if the stream is not at end-of-file
222 * \retval false otherwise
223 */
224 bool more() const;
225
226 /**
227 * \brief Reads a single element
228 * \details Reads an element of type \p T from the stream and stores
229 * it in \p x. Type \p T must be a numeric type, other types are not
230 * supported.
231 * \param[in] x a reference to a element of type T
232 * \tparam T the type of the element to read. This must be a numeric
233 * type
234 * \return a reference to this stream
235 */
236 template <class T>
237 inline BinaryInputStream& operator>> (T& x) {
238
6/12
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18502 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 18502 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 18502 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 166518 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 18502 times.
✗ Branch 17 not taken.
203547 return read(
239 (char*) &x, 1,
240 ItemSize<Numeric::Limits<T>::size>()
241 203547 );
242 }
243
244 /**
245 * \brief Reads opaque data
246 * \details Reads \p n bytes from the stream and stores them in the
247 * byte array \p ptr. Bytes read from the stream are stored without
248 * conversion in the array \p ptr.
249 * \param[in] ptr an array of bytes
250 * \param[in] n number of bytes to read
251 * \return a reference to this stream
252 */
253 inline BinaryInputStream& read_opaque_data(void* ptr, size_t n) {
254
1/2
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
25 input_->read((char*) ptr, (std::streamsize) n);
255 25 return *this;
256 }
257
258 /**
259 * \brief Reads opaque data
260 * \details Reads \p n elements of \p size bytes from the stream and
261 * stores them in the byte array \p ptr. Bytes read from the stream
262 * are stored without conversion in the array \p ptr.
263 * \param[in] ptr an array of bytes
264 * \param[in] size size of the elements to read
265 * \param[in] n number of elements to read
266 * \return a reference to this stream
267 */
268 inline BinaryInputStream& read_opaque_data(
269 void* ptr, size_t size, size_t n
270 ) {
271 return read_opaque_data(ptr, size * n);
272 }
273
274 /**
275 * \brief Reads an array of elements
276 * \details Reads \p n elements of type \p T from the stream and
277 * stores them in array pointed to by \p data. The array must be large
278 * enough to receive \p n elements. Type \p T must be a numeric type,
279 * other types are not supported.
280 * \param[in] data an array of at least \p n elements of type \p T.
281 * \param[in] n number of items to read
282 * \tparam T the type of the elements to read
283 * \return a reference to this stream
284 */
285 template <class T>
286 inline BinaryInputStream& read_array(T* data, size_t n) {
287 return read(
288 (char*) data, n,
289 ItemSize<Numeric::Limits<T>::size>()
290 );
291 }
292
293 /**
294 * \brief Starts reading a data record
295 * \details
296 * FORTRAN data files are structured into records,
297 * bounded by two integers indicating the size of
298 * the record. These two functions enable these integers
299 * to be read, and to use them as a validity check. If
300 * they differ, subsequent calls to OK() return false.
301 * Note that if set_had_record_markers() has been called
302 * with false, the records are supposed to be continuously
303 * written in the file (without markers).
304 */
305 void begin_record();
306
307 /**
308 * \brief Stops reading a data record
309 * \details This verifies that the record data is properly enclosed
310 * with the same marker, that is: the marker at the current stream
311 * position matches the marker found at the beginning of the record
312 * (see begin_record()).
313 */
314 void end_record();
315
316 /**
317 * \brief Reads an array of elements in a record
318 * \details Reads \p n elements of type \p T enclosed in a data record
319 * from the stream and stores them in array pointed to by \p data. The
320 * array must be large enough to receive \p n elements. Type \p T must
321 * be a numeric type, other types are not supported.
322 * \param[in] data an array of at least \p n elements of type \p T.
323 * \param[in] n number of items to read
324 * \return a reference to this stream
325 */
326 template <class T>
327 inline BinaryInputStream& read_record(T* data, size_t n) {
328 begin_record();
329 read(
330 (char*) data, n,
331 ItemSize<Numeric::Limits<T>::size>()
332 );
333 end_record();
334 return *this;
335 }
336
337 /**
338 * \brief Gets the position in the input sequence
339 * \return The absolute position in the stream
340 */
341 std::streamoff tell() const {
342 return input_->tellg();
343 }
344
345 /**
346 * \brief Sets the position in input sequence
347 * \param[in] pos the new absolute position in the stream
348 */
349 void seek(std::streamoff pos) {
350 input_->seekg(pos);
351 }
352
353 /**
354 * \brief Sets the position in input sequence
355 * \param[in] off offset value, relative to the \p dir parameter
356 * \param[in] dir the direction in which to seek:
357 * - ios_base::beg - beginning of the stream
358 * - ios_base::cur - current position in the stream
359 * - ios_base::end - end of the stream
360 */
361 void seek(std::streamoff off, std::ios_base::seekdir dir) {
362 input_->seekg(off, dir);
363 }
364
365 protected:
366 /**
367 * \brief Reads an array of elements of size 1
368 * \param[in] data an array of elements of size 1
369 * \param[in] n the number of elements to read
370 * \return a reference to this stream
371 */
372 inline BinaryInputStream& read(char* data, size_t n, ItemSize<1>) {
373 return read_opaque_data(data, n);
374 }
375
376 /**
377 * \brief Reads an array of elements of size 2
378 * \param[in] data an array of elements of size 2
379 * \param[in] n the number of elements to read
380 * \return a reference to this stream
381 */
382 BinaryInputStream& read(char* data, size_t n, ItemSize<2>);
383
384 /**
385 * \brief Reads an array of elements of size 4
386 * \param[in] data an array of elements of size 4
387 * \param[in] n the number of elements to read
388 * \return a reference to this stream
389 */
390 BinaryInputStream& read(char* data, size_t n, ItemSize<4>);
391
392 /**
393 * \brief Reads an array of elements of size 8
394 * \param[in] data an array of elements of size 8
395 * \param[in] n the number of elements to read
396 * \return a reference to this stream
397 */
398 BinaryInputStream& read(char* data, size_t n, ItemSize<8>);
399
400 private:
401 std::istream* input_;
402 bool owns_input_;
403 /** No pb encountered when reading the last record */
404 bool record_OK_;
405 /** Sentry integer preceding the current record */
406 Numeric::uint32 count1_;
407 /** Sentry integer following the current record */
408 Numeric::uint32 count2_;
409 /** Number of read records */
410 Numeric::int32 record_count_;
411 };
412
413 /************************************************************************/
414
415 /**
416 * \brief Binary output file
417 * \details
418 * Enables binary files to be written, while taking
419 * into account endian problems.
420 */
421 class GEOGRAM_API BinaryOutputStream : public BinaryStream {
422 public:
423 /**
424 * \brief Creates a new binary output stream
425 * \details This prepares the stream to write data to file \p
426 * file_name. Data will be stored with the given endianness
427 * \p stream_endian.
428 * \param[in] file_name path to the file to write
429 * \param[in] stream_endian the endianness of the stream:
430 * - GEO_LITTLE_ENDIAN makes the stream little-endian
431 * - GEO_BIG_ENDIAN makes the stream big-endian (the default)
432 */
433 BinaryOutputStream(
434 const std::string& file_name, int stream_endian = GEO_BIG_ENDIAN
435 );
436
437 /**
438 * \brief Creates a new binary output stream
439 * \details This prepares the BinaryOutputStream to write data to
440 * std::ostream \p output. Data will be stored with the given
441 * endianness \p stream_endian.
442 * \param[in] output the output stream to write to
443 * \param[in] stream_endian the endianness of the stream:
444 * - GEO_LITTLE_ENDIAN makes the stream little-endian
445 * - GEO_BIG_ENDIAN makes the stream big-endian (the default)
446 */
447 BinaryOutputStream(
448 std::ostream& output, int stream_endian = GEO_BIG_ENDIAN
449 );
450
451 /**
452 * \brief Deletes the output stream
453 * \details This closes any associated std::ostream and closes any
454 * associated file.
455 */
456 ~BinaryOutputStream();
457
458 /**
459 * \brief Gets the status of the stream
460 * \retval true if the stream is valid
461 * \retval false if an error occurred
462 */
463 bool OK() const;
464
465 /**
466 * \brief Writes a single element
467 * \param[in] x an element of type \p T which must be a numeric type,
468 * other types are not supported.
469 * \tparam T the type of the elements to read.
470 * \return a reference to this stream
471 */
472 template <class T>
473 inline BinaryOutputStream& operator<< (T x) {
474 return write(
475 (const char*) &x, 1,
476 ItemSize<Numeric::Limits<T>::size>()
477 );
478 }
479
480 /**
481 * \brief Writes opaque data
482 * \details Writes \p size bytes from the byte array \p ptr.
483 * \param[in] ptr an array of bytes
484 * \param[in] size number of bytes to write
485 * \return a reference to this stream
486 */
487 inline BinaryOutputStream& write_opaque_data(
488 const void* ptr, size_t size
489 ) {
490 output_->write((const char*) ptr, (std::streamsize) size);
491 count_ += size;
492 return *this;
493 }
494
495 /**
496 * \brief Writes opaque data
497 * \details Writes \p n elements of \p size bytes from the byte array
498 * \p ptr.
499 * \param[in] ptr an array of bytes
500 * \param[in] size size of the elements to write
501 * \param[in] n number of elements to write
502 * \return a reference to this stream
503 */
504 inline BinaryOutputStream& write_opaque_data(
505 const void* ptr, size_t size, size_t n
506 ) {
507 return write_opaque_data(ptr, size * n);
508 }
509
510 /**
511 * \brief Writes an array of elements
512 * \details Writes the first \p n elements of the array pointed
513 * to by \p data to the stream. Elements are of type \p T which must
514 * be a numeric type, other types are not supported.
515 * \param[in] data an array of at least \p n elements of type \p T.
516 * \param[in] n number of elements to write
517 * \tparam T the type of the elements to read.
518 * \return a reference to this stream
519 */
520 template <class T>
521 inline BinaryOutputStream& write_array(const T* data, size_t n) {
522 return write(
523 (const char*) data, n,
524 ItemSize<Numeric::Limits<T>::size>()
525 );
526 }
527
528 /**
529 * \brief Starts writing a data record
530 * \details
531 * FORTRAN data files are structured into records,
532 * bounded by two integers indicating the size of
533 * the record. These two functions enable these integers
534 * to be read, and to use them as a validity check. If
535 * they differ, subsequent calls to OK() return false.
536 * Note that if set_had_record_markers() has been called
537 * with false, the records are supposed to be continuously
538 * written in the file (without markers).
539 */
540 void begin_record();
541
542 /**
543 * \brief Stops writing a data record
544 * \details This encloses the data written between begin_record() and
545 * end_record() with a marker equal to the current position in the
546 * output stream.
547 */
548 void end_record();
549
550 /**
551 * \brief Writes an array of elements in a record
552 * \details Writes a record containing the first \p n elements
553 * of the array pointed to by \p data to the stream. Elements are of
554 * type \p T which must be a numeric type, other types are not
555 * supported.
556 * \param[in] data an array of at least \p n elements of type \p T.
557 * \param[in] n number of items to write
558 * \tparam T the type of the elements to read.
559 * \return a reference to this stream
560 */
561 template <class T>
562 BinaryOutputStream& write_record(const T* data, size_t n) {
563 begin_record();
564 write(
565 (const char*) data, n,
566 ItemSize<Numeric::Limits<T>::size>()
567 );
568 end_record();
569 return *this;
570 }
571
572 protected:
573 /**
574 * \brief Writes an array of elements of size 1
575 * \param[in] data an array of elements of size 1
576 * \param[in] n the number of elements to write
577 * \return a reference to this stream
578 */
579 BinaryOutputStream& write(const char* data, size_t n, ItemSize<1>) {
580 return write_opaque_data(data, n);
581 }
582
583 /**
584 * \brief Writes an array of elements of size 2
585 * \param[in] data an array of elements of size 2
586 * \param[in] n the number of elements to write
587 * \return a reference to this stream
588 */
589 BinaryOutputStream& write(const char* data, size_t n, ItemSize<2>);
590
591 /**
592 * \brief Writes an array of elements of size 4
593 * \param[in] data an array of elements of size 4
594 * \param[in] n the number of elements to write
595 * \return a reference to this stream
596 */
597 BinaryOutputStream& write(const char* data, size_t n, ItemSize<4>);
598
599 /**
600 * \brief Writes an array of elements of size 8
601 * \param[in] data an array of elements of size 8
602 * \param[in] n the number of elements to write
603 * \return a reference to this stream
604 */
605 BinaryOutputStream& write(const char* data, size_t n, ItemSize<8>);
606
607 private:
608 /**
609 * \brief Writes a record marker
610 * \param[in] value the value of the marker
611 */
612 void write_marker(Numeric::uint32 value);
613
614 std::ostream* output_;
615 bool owns_output_;
616 /** Size of the current record */
617 size_t count_;
618 /**
619 * Position of the current record relative to the
620 * beginning of the file. This is used to write count_
621 * at that location when end_record() is called.
622 */
623 std::streamoff pos_;
624 };
625
626 /************************************************************************/
627 }
628
629 #endif
630