GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/line_stream.h
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 33 33 100.0%
Functions: 11 11 100.0%
Branches: 13 28 46.4%

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 217 bool OK() const {
103 217 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 902790 bool eof() const {
112 902790 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 8357233 index_t nb_fields() const {
132 8357233 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 4 size_t line_number() const {
140 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 640346 char* field(index_t i) {
152
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 640346 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
640346 geo_assert(i < nb_fields());
153 640346 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 4459238 const char* field(index_t i) const {
165
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 4459238 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
4459238 geo_assert(i < nb_fields());
166 4459238 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 643590 signed_index_t field_as_int(index_t i) const {
181 643590 signed_index_t result = 0;
182
4/6
✓ Branch 1 taken 643590 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 643590 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 643589 times.
643590 if(!String::from_string(field(i), result)) {
183 1 conversion_error(i, "integer");
184 }
185 643589 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
4/6
✓ Branch 1 taken 532776 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 532776 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 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 1668704 double field_as_double(index_t i) const {
219 1668704 double result = 0;
220
3/4
✓ Branch 1 taken 1668704 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1668702 times.
1668704 if(!String::from_string(field(i), result)) {
221 2 conversion_error(i, "floating point");
222 }
223 1668702 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 1614163 bool field_matches(index_t i, const char* s) const {
238 1614163 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 66903 const char* current_line() const {
260 66903 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