GCC Code Coverage Report


Directory: ./
File: basic/string.cpp
Date: 2026-09-27 03:12:47
Exec Total Coverage
Lines: 33 100 33.0%
Functions: 7 17 41.2%
Branches: 24 112 21.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 #include <geogram/basic/string.h>
41 #include <ctype.h>
42 #include <stdarg.h>
43
44 namespace GEO {
45
46 /**
47 * \brief Builds the conversion error message
48 * \param[in] s the input string that could not be converted
49 * \param[in] type the expected destination type
50 * \return a string that contains the error message
51 */
52 ✗ static std::string conversion_error(
53 const std::string& s, const std::string& type
54 ) {
55 ✗ std::ostringstream out;
56 out << "Conversion error: cannot convert string '"
57 << s << "' to " << type;
58 ✗ return out.str();
59 ✗ }
60 }
61
62 namespace GEO {
63
64 namespace String {
65
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2357 times.
2357 void split_string(
67 const std::string& in,
68 char separator,
69 std::vector<std::string>& out,
70 bool skip_empty_fields
71 ) {
72 size_t length = in.length();
73 size_t start = 0;
74
2/2
✓ Branch 0 taken 4140 times.
✓ Branch 1 taken 2357 times.
6497 while(start < length) {
75 4140 size_t end = in.find(separator, start);
76
2/2
✓ Branch 0 taken 2098 times.
✓ Branch 1 taken 2042 times.
4140 if(end == std::string::npos) {
77 end = length;
78 }
79
2/2
✓ Branch 0 taken 3988 times.
✓ Branch 1 taken 152 times.
4140 if(!skip_empty_fields || (end - start > 0)) {
80 7976 out.push_back(in.substr(start, end - start));
81 }
82 4140 start = end + 1;
83 }
84 2357 }
85
86 ✗ void split_string(
87 const std::string& in,
88 const std::string& separator,
89 std::vector<std::string>& out,
90 bool skip_empty_fields
91 ) {
92 size_t length = in.length();
93 size_t start = 0;
94 ✗ while(start < length) {
95 size_t end = in.find(separator, start);
96 ✗ if(end == std::string::npos) {
97 end = length;
98 }
99 ✗ if(!skip_empty_fields || (end - start > 0)) {
100 ✗ out.push_back(in.substr(start, end - start));
101 }
102 ✗ start = end + separator.length();
103 }
104 ✗ }
105
106 ✗ bool split_string(
107 const std::string& in,
108 char separator,
109 std::string& left,
110 std::string& right
111 ) {
112 ✗ size_t p = in.find(separator);
113 ✗ if(p == std::string::npos) {
114 left = "";
115 right = "";
116 ✗ return false;
117 }
118 ✗ left = in.substr(0,p);
119 ✗ right = in.substr(p+1);
120 ✗ return true;
121 }
122
123
124 ✗ bool split_string(
125 const std::string& in,
126 const std::string& separator,
127 std::string& left,
128 std::string& right
129 ) {
130 size_t p = in.find(separator);
131 ✗ if(p == std::string::npos) {
132 left = "";
133 right = "";
134 ✗ return false;
135 }
136 ✗ left = in.substr(0,p);
137 ✗ right = in.substr(p+separator.length());
138 ✗ return true;
139 }
140
141 ✗ std::string join_strings(
142 const std::vector<std::string>& in,
143 char separator
144 ) {
145 std::string result;
146 ✗ for(unsigned int i = 0; i < in.size(); i++) {
147 ✗ if(result.length() != 0) {
148 result += separator;
149 }
150 result += in[i];
151 }
152 ✗ return result;
153 }
154
155 ✗ std::string join_strings(
156 const std::vector<std::string>& in,
157 const std::string& separator
158 ) {
159 std::string result;
160 ✗ for(unsigned int i = 0; i < in.size(); i++) {
161 ✗ if(result.length() != 0) {
162 result += separator;
163 }
164 result += in[i];
165 }
166 ✗ return result;
167 }
168
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1060 times.
1060 std::string to_lowercase(const std::string& in) {
170 std::string s = in;
171
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5057 times.
✓ Branch 2 taken 3997 times.
✓ Branch 3 taken 1060 times.
5057 for(unsigned int i = 0; i < s.length(); i++) {
172 3997 s[i] = char(tolower(s[i]));
173 }
174 1060 return s;
175 }
176
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2290 times.
2290 std::string to_uppercase(const std::string& in) {
178 std::string s = in;
179
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13587 times.
✓ Branch 2 taken 11297 times.
✓ Branch 3 taken 2290 times.
13587 for(unsigned int i = 0; i < s.length(); i++) {
180 11297 s[i] = char(toupper(s[i]));
181 }
182 2290 return s;
183 }
184
185 ✗ std::string quote(const std::string& s, char quotes) {
186 ✗ return char_to_string(quotes) + s + char_to_string(quotes);
187 }
188
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 778 times.
778 bool string_starts_with(
190 const std::string& haystack, const std::string& needle
191 ) {
192 778 return haystack.compare(0, needle.length(), needle) == 0;
193 }
194
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 bool string_ends_with(
196 const std::string& haystack, const std::string& needle
197 ) {
198 size_t l1 = haystack.length();
199 size_t l2 = needle.length();
200
3/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 2 times.
38 return l1 > l2 && haystack.compare(l1 - l2, l1, needle) == 0;
201 }
202
203 6643 std::string format(const char* format, ...) {
204 size_t length = 0;
205
206 // Determine required length
207 va_list arg_ptr;
208 6643 va_start(arg_ptr, format);
209 6643 length = size_t(vsnprintf(nullptr, 0, format, arg_ptr));
210 6643 va_end(arg_ptr);
211
212 // Create the string of required length and sprintf() into it
213 std::string result(length,'*');
214 6643 va_start(arg_ptr, format);
215 6643 vsnprintf(
216 const_cast<char*>(result.c_str()), length+1, format, arg_ptr
217 );
218 6643 va_end(arg_ptr);
219
220 6643 return result;
221 }
222
223
1/2
✓ Branch 0 taken 684 times.
✗ Branch 1 not taken.
684 std::string format_time(double seconds, bool HMS_only) {
224
225 std::string result;
226
1/2
✓ Branch 0 taken 684 times.
✗ Branch 1 not taken.
684 if(!HMS_only) {
227
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
2052 result = String::to_display_string(seconds) + "s";
228 }
229
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 684 times.
684 if(seconds >= 60.0) {
231 ✗ while(!HMS_only && result.length() <= 10) {
232 result += " ";
233 }
234 ✗ int S = int(seconds);
235 ✗ int H = S / 3600;
236 ✗ S = S % 3600;
237 ✗ int M = S / 60;
238 ✗ S = S % 60;
239 ✗ result += String::format("(%02d:%02d:%02d)",H,M,S);
240 }
241
242 684 return result;
243 }
244
245 // Reference: https://stackoverflow.com/questions/148403/
246 // utf8-to-from-wide-char-conversion-in-stl
247
248 ✗ std::string wchar_to_UTF8(const wchar_t* in) {
249 std::string out;
250 unsigned int codepoint = 0;
251 ✗ for (; *in != 0; ++in) {
252 ✗ if (*in >= 0xd800 && *in <= 0xdbff) {
253 ✗ codepoint = (unsigned int)(
254 ✗ ((*in - 0xd800) << 10) + 0x10000
255 );
256 } else {
257 ✗ if (*in >= 0xdc00 && *in <= 0xdfff) {
258 ✗ codepoint |= (unsigned int)(*in - 0xdc00);
259 } else {
260 codepoint = (unsigned int)(*in);
261 }
262
263 ✗ if (codepoint <= 0x7f) {
264 out.append(1, char(codepoint));
265 ✗ } else if (codepoint <= 0x7ff) {
266 ✗ out.append(1, char(0xc0 | ((codepoint >> 6) & 0x1f)));
267 ✗ out.append(1, char(0x80 | (codepoint & 0x3f)));
268 ✗ } else if (codepoint <= 0xffff) {
269 ✗ out.append(1, char(0xe0 | ((codepoint >> 12) & 0x0f)));
270 ✗ out.append(1, char(0x80 | ((codepoint >> 6) & 0x3f)));
271 ✗ out.append(1, char(0x80 | (codepoint & 0x3f)));
272 } else {
273 ✗ out.append(1, char(0xf0 | ((codepoint >> 18) & 0x07)));
274 ✗ out.append(1, char(0x80 | ((codepoint >> 12) & 0x3f)));
275 ✗ out.append(1, char(0x80 | ((codepoint >> 6) & 0x3f)));
276 ✗ out.append(1, char(0x80 | (codepoint & 0x3f)));
277 }
278 codepoint = 0;
279 }
280 }
281 ✗ return out;
282 }
283
284 /********************************************************************/
285
286 ✗ ConversionError::ConversionError(
287 const std::string& s, const std::string& type
288 ✗ ) :
289 ✗ std::logic_error(conversion_error(s, type)) {
290 ✗ }
291
292 ✗ const char* ConversionError::what() const GEO_NOEXCEPT {
293 ✗ return std::logic_error::what();
294 }
295 }
296 }
297