GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/string.cpp
Date: 2026-09-07 02:25:23
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 2293 times.
2293 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 4042 times.
✓ Branch 1 taken 2293 times.
6335 while(start < length) {
75 4042 size_t end = in.find(separator, start);
76
2/2
✓ Branch 0 taken 2040 times.
✓ Branch 1 taken 2002 times.
4042 if(end == std::string::npos) {
77 end = length;
78 }
79
2/2
✓ Branch 0 taken 3892 times.
✓ Branch 1 taken 150 times.
4042 if(!skip_empty_fields || (end - start > 0)) {
80 7784 out.push_back(in.substr(start, end - start));
81 }
82 4042 start = end + 1;
83 }
84 2293 }
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 1032 times.
1032 std::string to_lowercase(const std::string& in) {
170 std::string s = in;
171
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4925 times.
✓ Branch 2 taken 3893 times.
✓ Branch 3 taken 1032 times.
4925 for(unsigned int i = 0; i < s.length(); i++) {
172 3893 s[i] = char(tolower(s[i]));
173 }
174 1032 return s;
175 }
176
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2236 times.
2236 std::string to_uppercase(const std::string& in) {
178 std::string s = in;
179
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13229 times.
✓ Branch 2 taken 10993 times.
✓ Branch 3 taken 2236 times.
13229 for(unsigned int i = 0; i < s.length(); i++) {
180 10993 s[i] = char(toupper(s[i]));
181 }
182 2236 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 758 times.
758 bool string_starts_with(
190 const std::string& haystack, const std::string& needle
191 ) {
192 758 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 6640 std::string format(const char* format, ...) {
204 size_t length = 0;
205
206 // Determine required length
207 va_list arg_ptr;
208 6640 va_start(arg_ptr, format);
209 6640 length = size_t(vsnprintf(nullptr, 0, format, arg_ptr));
210 6640 va_end(arg_ptr);
211
212 // Create the string of required length and sprintf() into it
213 std::string result(length,'*');
214 6640 va_start(arg_ptr, format);
215 6640 vsnprintf(
216 const_cast<char*>(result.c_str()), length+1, format, arg_ptr
217 );
218 6640 va_end(arg_ptr);
219
220 6640 return result;
221 }
222
223
1/2
✓ Branch 0 taken 640 times.
✗ Branch 1 not taken.
640 std::string format_time(double seconds, bool HMS_only) {
224
225 std::string result;
226
1/2
✓ Branch 0 taken 640 times.
✗ Branch 1 not taken.
640 if(!HMS_only) {
227
1/2
✓ Branch 1 taken 640 times.
✗ Branch 2 not taken.
1920 result = String::to_display_string(seconds) + "s";
228 }
229
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 640 times.
640 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 640 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