GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/istream_helper.hpp
Date: 2026-10-18 00:09:19
Exec Total Coverage
Lines: 89 94 94.7%
Functions: 14 14 100.0%
Branches: 39 70 55.7%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2 // SPDX-License-Identifier: MIT
3 /*!
4 * \file
5 * \ingroup Common
6 * \brief Helper for parsing data from input streams.
7 */
8 #ifndef GRIDFORMAT_COMMON_ISTREAM_HELPER_HPP_
9 #define GRIDFORMAT_COMMON_ISTREAM_HELPER_HPP_
10
11 #include <cmath>
12 #include <string>
13 #include <istream>
14 #include <optional>
15 #include <utility>
16 #include <concepts>
17 #include <iterator>
18 #include <limits>
19
20 #include <gridformat/common/exceptions.hpp>
21
22 namespace GridFormat {
23
24 /*!
25 * \ingroup Common
26 * \brief Helper for parsing data from input streams.
27 */
28 class InputStreamHelper {
29 public:
30 static constexpr std::size_t default_chunk_size = 5000;
31 static constexpr const char* default_whitespace_chars = " \t\r\n";
32
33
9/18
✓ Branch 1 taken 9399 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 9399 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1576 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 1576 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 782 times.
✗ Branch 16 not taken.
✓ Branch 19 taken 782 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1948 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 1948 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 25697 times.
✗ Branch 30 not taken.
118206 explicit InputStreamHelper(std::istream& s, std::string whitespace_chars = default_whitespace_chars)
34 39402 : _stream{s}
35 39402 , _whitespace_chars{std::move(whitespace_chars)}
36 39402 {}
37
38 //! Read a chunk of characters from the stream into the given buffer
39 2169066 void read_chunk_to(std::string& buffer, const std::size_t chunk_size = default_chunk_size) {
40
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2169065 times.
2169066 if (is_end_of_file())
41
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw IOError("End of file already reached");
42 2169065 buffer.resize(chunk_size);
43 2169065 _stream.read(buffer.data(), chunk_size);
44 2169065 _stream.clear();
45 2169065 buffer.resize(_stream.gcount());
46 2169065 }
47
48 //! Read a chunk of characters from the stream
49 1744488 std::string read_chunk(const std::size_t chunk_size = default_chunk_size) {
50
1/2
✓ Branch 1 taken 1744488 times.
✗ Branch 2 not taken.
1744488 std::string tmp(chunk_size, ' ');
51
2/2
✓ Branch 1 taken 1744487 times.
✓ Branch 2 taken 1 times.
1744488 read_chunk_to(tmp, chunk_size);
52 1744487 return tmp;
53 1 }
54
55 //! Move the position forward until any of the given characters is found or EOF is reached
56 423133 bool shift_until_any_of(const std::string& chars, std::optional<std::size_t> max_chars = {}) {
57 423133 std::string tmp_buffer;
58 423133 std::size_t char_count = 0;
59 423133 const auto max_num_chars = max_chars.value_or(std::numeric_limits<std::size_t>::max());
60
61
2/2
✓ Branch 0 taken 422983 times.
✓ Branch 1 taken 13956 times.
436939 while (char_count < max_num_chars) {
62
1/2
✓ Branch 2 taken 422983 times.
✗ Branch 3 not taken.
422983 read_chunk_to(tmp_buffer, std::min(default_chunk_size, max_num_chars - char_count));
63 422983 const auto str_pos = tmp_buffer.find_first_of(chars);
64
2/2
✓ Branch 0 taken 408748 times.
✓ Branch 1 taken 14235 times.
422983 if (str_pos != std::string::npos) {
65 408748 const auto delta_pos = tmp_buffer.size() - str_pos;
66
1/2
✓ Branch 1 taken 408748 times.
✗ Branch 2 not taken.
408748 shift_by(-delta_pos);
67 408748 return true;
68 }
69
70
3/4
✓ Branch 1 taken 14235 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 429 times.
✓ Branch 4 taken 13806 times.
14235 if (is_end_of_file())
71 429 return false;
72
73 13806 char_count += tmp_buffer.size();
74 }
75
76 13956 return false;
77 423133 }
78
79 //! Read characters from the stream until any of the given characters is found or EOF is reached
80 338247 std::string read_until_any_of(const std::string& chars, std::optional<std::size_t> max_chars = {}) {
81 338247 const auto p0 = position();
82 338247 shift_until_any_of(chars, max_chars);
83 338247 const auto p1 = position();
84 338247 seek_position(p0);
85 338247 return read_chunk(p1 - p0);
86 }
87
88 //! Move the position forward until a character that is none of the given ones is found or EOF is reached
89 486141 bool shift_until_not_any_of(const std::string& chars) {
90 486141 std::string tmp_buffer;
91 while (true) {
92
2/2
✓ Branch 1 taken 486140 times.
✓ Branch 2 taken 1 times.
486141 tmp_buffer = read_chunk();
93 486140 const auto pos = tmp_buffer.find_first_not_of(chars);
94
1/2
✓ Branch 0 taken 486140 times.
✗ Branch 1 not taken.
486140 if (pos != std::string::npos) {
95 486140 const auto delta_pos = tmp_buffer.size() - pos;
96
1/2
✓ Branch 1 taken 486140 times.
✗ Branch 2 not taken.
486140 shift_by(-delta_pos);
97 486140 return true;
98 }
99
100 ✗ if (is_end_of_file())
101 ✗ return false;
102 ✗ }
103 486141 }
104
105 //! Read from the stream until a character not matching any of the given characters is found or EOF is reached
106 11741 std::string read_until_not_any_of(const std::string& chars) {
107 11741 const auto p0 = position();
108 11741 shift_until_not_any_of(chars);
109 11741 const auto p1 = position();
110 11741 seek_position(p0);
111 11741 return read_chunk(p1 - p0);
112 }
113
114 //! Move the position until the given string is found or EOF is reached
115 1092 bool shift_until_substr(const std::string& substr) {
116 1092 const std::streamsize delta = -substr.size();
117 1092 const auto chunk_size = std::max(substr.size()*10, default_chunk_size);
118 1092 std::string chunk;
119 while (true) {
120
1/2
✓ Branch 1 taken 1595 times.
✗ Branch 2 not taken.
1595 const auto cur_pos = position();
121
1/2
✓ Branch 1 taken 1595 times.
✗ Branch 2 not taken.
1595 read_chunk_to(chunk, chunk_size);
122 1595 const auto substr_pos = chunk.find(substr);
123
2/2
✓ Branch 0 taken 1092 times.
✓ Branch 1 taken 503 times.
1595 if (substr_pos != std::string::npos) {
124
1/2
✓ Branch 1 taken 1092 times.
✗ Branch 2 not taken.
1092 seek_position(cur_pos + substr_pos);
125 1092 return true;
126 }
127
128
2/4
✓ Branch 1 taken 503 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 503 times.
503 if (is_end_of_file())
129 ✗ return false;
130
131 // shift back a bit in case the substr lies at chunk boundaries
132
1/2
✓ Branch 1 taken 503 times.
✗ Branch 2 not taken.
503 shift_by(delta);
133 503 }
134 1092 }
135
136 //! Skip characters considered whitespace
137 474400 void shift_whitespace() {
138 474400 shift_until_not_any_of(_whitespace_chars);
139 474399 }
140
141 //! Skip characters until a whitespace is found
142 void shift_until_whitespace() {
143 shift_until_any_of(_whitespace_chars);
144 }
145
146 //! Jump forward in the stream by n characters
147 959175 void shift_by(std::streamsize n) {
148 959175 seek_position(position() + n);
149 959175 }
150
151 //! Return the current position in the stream
152 2027307 std::streamsize position() {
153 2027307 _stream.clear();
154
1/2
✓ Branch 1 taken 2027307 times.
✗ Branch 2 not taken.
2027307 return _stream.tellg();
155 }
156
157 //! Jump the the requested position
158 1782855 void seek_position(std::streamsize pos) {
159 1782855 _stream.clear();
160
1/2
✓ Branch 2 taken 1782855 times.
✗ Branch 3 not taken.
1782855 _stream.seekg(pos);
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1782855 times.
1782855 if (_stream.fail())
162 ✗ throw SizeError("Given position is beyond EOF");
163 1782855 }
164
165 //! Return true if no more characters can be read from the stream
166 2392488 bool is_end_of_file() {
167 2392488 _stream.peek();
168 2392488 const bool end = _stream.eof();
169 2392488 _stream.clear();
170 2392488 return end;
171 }
172
173 1576 operator std::istream&() {
174 1576 return _stream;
175 }
176
177 private:
178 std::istream& _stream;
179 std::string _whitespace_chars;
180 };
181
182 } // end namespace GridFormat
183
184 #endif // GRIDFORMAT_COMMON_ISTREAM_HELPER_HPP_
185