GCC Code Coverage Report


Directory: src/
File: src/misc/jstring.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 158 165 95.8%
Functions: 8 9 88.9%
Branches: 57 63 90.5%

Line Branch Exec Source
1 /* Copyright 2025 Jonathan S. Arney
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://github.com/jarney/gyoji/blob/master/LICENSE
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <algorithm>
16 #include <gyoji-misc/jstring.hpp>
17
18 using namespace Gyoji::misc;
19
20 24646 std::vector<std::string> Gyoji::misc::string_split(const std::string &str, const std::string &delimiter)
21 {
22 24646 std::vector<std::string> ret;
23 24646 size_t pos = 0;
24 while (true) {
25 25000 size_t newpos = str.find(delimiter, pos);
26
2/2
✓ Branch 0 taken 24646 times.
✓ Branch 1 taken 354 times.
25000 if (newpos == std::string::npos) {
27 24646 break;
28 }
29 354 ret.push_back(str.substr(pos, newpos-pos));
30 354 pos = newpos + delimiter.size();
31 354 }
32
1/2
✓ Branch 0 taken 24646 times.
✗ Branch 1 not taken.
24646 if (pos == std::string::npos) {
33 }
34 else {
35 24646 ret.push_back(str.substr(pos));
36 }
37 24646 return ret;
38 }
39
40 std::string
41 Gyoji::misc::join_nonempty(const std::string &a, const std::string & b, const std::string delimiter)
42 {
43 std::string ret;
44 if (a.size() == 0) {
45 return b;
46 }
47 else {
48 return a + delimiter + b;
49 }
50 }
51
52 468 std::string Gyoji::misc::join(const std::vector<std::string> & list, std::string delimiter)
53 {
54 468 std::string ret;
55
56 468 bool first = true;
57
2/2
✓ Branch 4 taken 806 times.
✓ Branch 5 taken 468 times.
1274 for (const auto & s : list) {
58
2/2
✓ Branch 0 taken 372 times.
✓ Branch 1 taken 434 times.
806 if (!first) {
59 372 ret = ret + delimiter;
60 }
61 806 first = false;
62 806 ret = ret + s;
63 }
64
65 468 return ret;
66 }
67
68 894 bool Gyoji::misc::startswith(const std::string & s, const std::string & prefix)
69 {
70 894 size_t found_pos = s.find(prefix);
71
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 832 times.
894 if (found_pos == 0) {
72 62 return true;
73 }
74 832 return false;
75 }
76 1620 bool Gyoji::misc::endswith(const std::string & s, const std::string & suffix)
77 {
78 1620 size_t found_pos = s.find(suffix);
79
6/6
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 1496 times.
✓ Branch 4 taken 122 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 122 times.
✓ Branch 7 taken 1498 times.
1620 if (found_pos != std::string::npos && found_pos == s.size() - suffix.size()) {
80 122 return true;
81 }
82 1498 return false;
83 }
84
85 std::string
86 292 Gyoji::misc::string_remove(const std::string & str, const std::string & remove)
87 {
88 292 std::string ret(str);
89 while (true) {
90 464 size_t start_pos = ret.find(remove);
91 // String is not found at all.
92
2/2
✓ Branch 0 taken 292 times.
✓ Branch 1 taken 172 times.
464 if (start_pos == std::string::npos) {
93 292 break;
94 }
95
96 172 ret.erase(start_pos, remove.length());
97 172 }
98 292 return ret;
99
100 }
101
102 std::string
103 236 Gyoji::misc::string_replace_start(std::string str, const std::string from, const std::string to)
104 {
105 236 std::string ret(str);
106 236 size_t start_pos = str.find(from);
107
108 // String is not found at all.
109
2/2
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 16 times.
236 if (start_pos == std::string::npos) {
110 220 return ret;
111 }
112 // If the string is not found at the start, so do nothing.
113
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if (start_pos != 0) {
114 2 return ret;
115 }
116
117 // Replace the string.
118 14 ret.replace(start_pos, from.length(), to);
119 14 return ret;
120 }
121
122 // TODO: These need to be implemented and
123 // must guarantee that they are reversible
124 // in all cases.
125
126 26 bool Gyoji::misc::string_c_escape(std::string & escaped_string, const std::string & unescaped_string, bool is_char)
127 {
128 // Take an un-escaped string and insert the \n, \r, \e
129 // escapes exactly as if it were a string literal expressed
130 // in C. Choose exactly one set of escapes as the 'core'
131 // standard set from C, but no more and no less. This should
132 // be the least common denominator for escapes.
133
2/2
✓ Branch 5 taken 260 times.
✓ Branch 6 taken 26 times.
286 for (char c : unescaped_string) {
134
11/11
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 14 times.
✓ Branch 8 taken 6 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 202 times.
260 switch (c) {
135 6 case 0x07:
136 6 escaped_string.push_back('\\');
137 6 escaped_string.push_back('a');
138 6 break;
139 4 case 0x08:
140 4 escaped_string.push_back('\\');
141 4 escaped_string.push_back('b');
142 4 break;
143 4 case 0x1b:
144 4 escaped_string.push_back('\\');
145 4 escaped_string.push_back('e');
146 4 break;
147 4 case 0x0c:
148 4 escaped_string.push_back('\\');
149 4 escaped_string.push_back('f');
150 4 break;
151 8 case 0x0a:
152 8 escaped_string.push_back('\\');
153 8 escaped_string.push_back('n');
154 8 break;
155 4 case 0x0d:
156 4 escaped_string.push_back('\\');
157 4 escaped_string.push_back('r');
158 4 break;
159 4 case 0x09:
160 4 escaped_string.push_back('\\');
161 4 escaped_string.push_back('t');
162 4 break;
163 14 case 0x27:
164
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 if (is_char) {
165 8 escaped_string.push_back('\\');
166 8 escaped_string.push_back('\'');
167 }
168 else {
169 6 escaped_string.push_back('\'');
170 }
171 14 break;
172 6 case 0x22:
173
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (is_char) {
174 4 escaped_string.push_back('\"');
175 }
176 else {
177 2 escaped_string.push_back('\\');
178 2 escaped_string.push_back('\"');
179 }
180 6 break;
181 4 case 0x5c:
182 4 escaped_string.push_back('\\');
183 4 escaped_string.push_back('\\');
184 4 break;
185 202 default:
186 202 escaped_string.push_back(c);
187 202 break;
188 }
189 }
190 26 return true;
191 }
192
193 bool
194 28 Gyoji::misc::string_c_unescape(std::string & unescaped_string, size_t & location, const std::string & escaped_string, bool is_char)
195 {
196 // Take the 'traditional' C escape sequences
197 // and turn them into their 'traditional' counterparts.
198 #define NORMAL 0
199 #define IN_ESCAPE 1
200
201 28 location = 0;
202
203 28 int state = NORMAL;
204
2/2
✓ Branch 4 taken 362 times.
✓ Branch 5 taken 26 times.
388 for (char c : escaped_string) {
205
2/3
✓ Branch 0 taken 312 times.
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
362 switch (state) {
206 312 case NORMAL:
207
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 262 times.
312 if (c == '\\') {
208 50 state = IN_ESCAPE;
209 }
210 else {
211 262 unescaped_string.push_back(c);
212 }
213 312 break;
214 50 case IN_ESCAPE:
215
11/11
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
50 switch (c) {
216 4 case '\\':
217 4 unescaped_string.push_back(c);
218 4 state = NORMAL;
219 4 break;
220 6 case 'a':
221 6 unescaped_string.push_back(0x07);
222 6 state = NORMAL;
223 6 break;
224 4 case 'b':
225 4 unescaped_string.push_back(0x08);
226 4 state = NORMAL;
227 4 break;
228 4 case 'e':
229 4 unescaped_string.push_back(0x1b);
230 4 state = NORMAL;
231 4 break;
232 4 case 'f':
233 4 unescaped_string.push_back(0x0c);
234 4 state = NORMAL;
235 4 break;
236 8 case 'n':
237 8 unescaped_string.push_back(0x0a);
238 8 state = NORMAL;
239 8 break;
240 4 case 'r':
241 4 unescaped_string.push_back(0x0d);
242 4 state = NORMAL;
243 4 break;
244 4 case 't':
245 4 unescaped_string.push_back(0x09);
246 4 state = NORMAL;
247 4 break;
248 8 case '\'':
249
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (is_char) {
250 8 unescaped_string.push_back(0x27);
251 8 state = NORMAL;
252 }
253 else {
254 2 return false;
255 }
256 8 break;
257 2 case '\"':
258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (is_char) {
259 return false;
260 }
261 else {
262 2 unescaped_string.push_back(0x22);
263 2 state = NORMAL;
264 }
265 2 break;
266 2 default:
267 // This is not a valid escape sequence.
268 2 return false;
269
270 }
271 }
272 360 location++;
273 }
274
275 26 return true;
276 }
277
278