| 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 |