GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/line_stream.h
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 25 25 100.0%
Functions: 5 5 100.0%
Branches: 56 238 23.5%

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_LINE_STREAM
41 #define GEOGRAM_BASIC_LINE_STREAM
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/assert.h>
45 #include <geogram/basic/numeric.h>
46 #include <geogram/basic/string.h>
47 #include <cstring>
48 #include <stdio.h>
49
50 /**
51 * \file geogram/basic/line_stream.h
52 * \brief Utility class to read a file line per line and parsing
53 * fields from each line
54 */
55
56 namespace GEO {
57
58 /**
59 * \brief Reads an ASCII file line per line
60 * \details LineInput reads an ASCII file line by line and splits
61 * the line into a list of white space separated fields that can be
62 * accessed individually or converted to numeric values.
63 *
64 * Functions field_as_int() and field_as_double() throw exceptions when
65 * they cannot convert a field to a integer or floating point value, so
66 * it is safe to wrap the LineNumber usage in a try / catch block as
67 * follows:
68 *
69 * \code
70 * try {
71 * LineInput in(filename);
72 * while( !in.eof() && in.get_line() ) {
73 * in.get_fields();
74 * double d = in.field_as_double(2);
75 * }
76 * }
77 * catch(const std::logic_error& ex) {
78 * std::cerr << "Got an error: " << ex.what() << std::endl;
79 * }
80 * \endcode
81 */
82 class GEOGRAM_API LineInput {
83 public:
84 /**
85 * \brief Creates a new line reader from a file
86 * \details This open the file \p filename for reading and prepares to
87 * read it line by line. If the file could not be opened, OK() will
88 * return false;
89 * \param[in] filename the name of the file to read
90 */
91 LineInput(const std::string& filename);
92
93 /**
94 * \brief Destroys the line reader
95 * \details This closes the current input file.
96 */
97 ~LineInput();
98
99 /**
100 * \brief Checks if the line reader is ready to read.
101 */
102 bool OK() const {
103
6/18
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 24 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 165 times.
✓ Branch 17 taken 1 times.
215 return ok_;
104 }
105
106 /**
107 * \brief Checks if line reader has reached the end of the input stream
108 * \retval true if the stream is at end
109 * \retval false otherwise
110 */
111 bool eof() const {
112 866794 return feof(F_) ? true : false;
113 }
114
115 /**
116 * \brief Reads a new line
117 * \details Reads a new line from the input stream. Function
118 * get_fields() must be called if you need to access to individual
119 * fields in the line with field() and its typed variants.
120 * \retval true if a line could be read
121 * \retval false otherwise.
122 */
123 bool get_line();
124
125 /**
126 * \brief Gets the number of fields in the current line
127 * \details Function get_fields() must be called once after get_line()
128 * before calling this function, otherwise the result is undefined.
129 * \return the number of fields in the current line
130 */
131 index_t nb_fields() const {
132 return index_t(field_.size());
133 }
134
135 /**
136 * \brief Returns the current line number
137 * \details If no line has been read so far, line_number() returns 0.
138 */
139 size_t line_number() const {
140
4/74
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✓ Branch 76 taken 1 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 1 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 1 times.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✓ Branch 100 taken 1 times.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
4 return line_num_;
141 }
142
143 /**
144 * \brief Gets a line field as a modifiable string
145 * \details The function returns the field at index \p i. Function
146 * get_fields() must be called once after get_line() before calling
147 * this function, otherwise the result is undefined.
148 * \param[in] i the index of the field
149 * \return the modifiable pointer to field string at index \p i
150 */
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 616346 times.
616346 char* field(index_t i) {
152
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 616346 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
616346 geo_assert(i < nb_fields());
153 616346 return field_[i];
154 }
155
156 /**
157 * \brief Gets a line field as a non-modifiable string
158 * \details The function returns the field at index \p i. Function
159 * get_fields() must be called once after get_line() before calling
160 * this function, otherwise the result is undefined.
161 * \param[in] i the index of the field
162 * \return the const pointer to field string at index \p i
163 */
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4291262 times.
4291262 const char* field(index_t i) const {
165
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4291262 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
4291262 geo_assert(i < nb_fields());
166 4291262 return field_[i];
167 }
168
169 /**
170 * \brief Gets a line field as an integer.
171 * \details The function returns the field at index \p i converted to
172 * an integer. Function get_fields() must be called once after
173 * get_line() before calling this function, otherwise the result is
174 * undefined.
175 * \param[in] i the index of the field
176 * \return the integer value of the field at index \p i
177 * \exception std::logic_error if the field cannot be converted to an
178 * integer value
179 */
180 619590 signed_index_t field_as_int(index_t i) const {
181 619590 signed_index_t result = 0;
182
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 619589 times.
619590 if(!String::from_string(field(i), result)) {
183 1 conversion_error(i, "integer");
184 }
185 619589 return result;
186 }
187
188 /**
189 * \brief Gets a line field as an unsigned integer.
190 * \details The function returns the field at index \p i converted to
191 * an unsigned integer. Function get_fields() must be called once after
192 * get_line() before calling this function, otherwise the result is
193 * undefined.
194 * \param[in] i the index of the field
195 * \return the unsigned integer value of the field at index \p i
196 * \exception std::logic_error if the field cannot be converted to an
197 * unsigned integer value
198 */
199 532776 index_t field_as_uint(index_t i) const {
200 532776 index_t result = 0;
201
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 532774 times.
532776 if(!String::from_string(field(i), result)) {
202 2 conversion_error(i, "unsigned integer");
203 }
204 532774 return result;
205 }
206
207 /**
208 * \brief Gets a line field as a double.
209 * \details The function returns the field at index \p i converted to
210 * a double. Function get_fields() must be called once after
211 * get_line() before calling this function, otherwise the result is
212 * undefined.
213 * \param[in] i the index of the field
214 * \return the floating point value of the field at index \p i
215 * \exception std::logic_error if the field cannot be converted to a
216 * floating point value
217 */
218 1608728 double field_as_double(index_t i) const {
219 1608728 double result = 0;
220
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1608726 times.
1608728 if(!String::from_string(field(i), result)) {
221 2 conversion_error(i, "floating point");
222 }
223 1608726 return result;
224 }
225
226 /**
227 * \brief Compares a field with a string.
228 * \details The function compares the field at index \p i with string
229 * \p s and returns \c true if they are equal. Function get_fields()
230 * must be called once after get_line() before calling this function,
231 * otherwise the result is undefined.
232 * \param[in] i the index of the field
233 * \param[in] s the string to compare the field to
234 * \retval true if field at index \p i equals string \p s
235 * \retval false otherwise
236 */
237 bool field_matches(index_t i, const char* s) const {
238
36/124
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✓ Branch 44 taken 4 times.
✗ Branch 45 not taken.
✓ Branch 46 taken 4 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 4 times.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✓ Branch 52 taken 4 times.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 59 taken 4 times.
✗ Branch 60 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✓ Branch 77 taken 452 times.
✗ Branch 78 not taken.
✓ Branch 79 taken 64 times.
✓ Branch 80 taken 388 times.
✓ Branch 82 taken 388 times.
✗ Branch 83 not taken.
✓ Branch 84 taken 64 times.
✓ Branch 85 taken 324 times.
✓ Branch 87 taken 324 times.
✗ Branch 88 not taken.
✓ Branch 89 taken 192 times.
✓ Branch 90 taken 132 times.
✓ Branch 92 taken 132 times.
✗ Branch 93 not taken.
✓ Branch 94 taken 131 times.
✓ Branch 95 taken 1 times.
✓ Branch 97 taken 24 times.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✓ Branch 100 taken 24 times.
✓ Branch 102 taken 727776 times.
✗ Branch 103 not taken.
✓ Branch 104 taken 341182 times.
✓ Branch 105 taken 386594 times.
✓ Branch 107 taken 386594 times.
✗ Branch 108 not taken.
✓ Branch 109 taken 179903 times.
✓ Branch 110 taken 206691 times.
✓ Branch 112 taken 206691 times.
✗ Branch 113 not taken.
✓ Branch 114 taken 205837 times.
✓ Branch 115 taken 854 times.
✓ Branch 117 taken 205813 times.
✗ Branch 118 not taken.
✓ Branch 119 taken 13209 times.
✓ Branch 120 taken 192604 times.
✓ Branch 122 taken 1944 times.
✗ Branch 123 not taken.
✓ Branch 124 taken 1931 times.
✓ Branch 125 taken 13 times.
✓ Branch 127 taken 13 times.
✗ Branch 128 not taken.
✗ Branch 129 not taken.
✓ Branch 130 taken 13 times.
✗ Branch 132 not taken.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✗ Branch 135 not taken.
✗ Branch 137 not taken.
✗ Branch 138 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 142 not taken.
✗ Branch 143 not taken.
✗ Branch 144 not taken.
✗ Branch 145 not taken.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✗ Branch 149 not taken.
✗ Branch 150 not taken.
✗ Branch 152 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 155 not taken.
1529319 return strcmp(field(i), s) == 0;
239 }
240
241 /**
242 * \brief Splits the current line into fields.
243 * \details The function uses \p separators to split the
244 * current line into individual fields that can be accessed
245 * by field() and its typed variants.
246 * \param[in] separators a string that contains all
247 * the characters considered as separators.
248 * \see field()
249 */
250 void get_fields(const char* separators = " \t\r\n");
251
252 /**
253 * \brief Gets the current line.
254 * \details If get_fields() was called, then an end-of-string
255 * marker '\0' is present at the end of the first field.
256 * \return a const pointer to the internal buffer that stores
257 * the current line
258 */
259 const char* current_line() const {
260 return line_;
261 }
262
263 private:
264 /**
265 * \brief Throws a conversion error.
266 * \details This function is called by field_as_int() and
267 * field_as_double() when the field \p index cannot be converted to
268 * the desired type \p type.
269 * \param[in] index index of the erroneous field.
270 * \param[in] type the expected type.
271 */
272 GEO_NORETURN_DECL void conversion_error(
273 index_t index, const char* type
274 ) const GEO_NORETURN ;
275
276 /**
277 * \brief Defines the maximum size of a line
278 */
279 static constexpr index_t MAX_LINE_LEN = 65535;
280
281 FILE* F_;
282 std::string file_name_;
283 size_t line_num_;
284 char line_[MAX_LINE_LEN];
285 std::vector<char*> field_;
286 bool ok_;
287 };
288 }
289
290 #endif
291