13 #include <initializer_list> 17 #include <string_view> 20 #include <fcitx-utils/fcitxutils_export.h> 28 concatPieces(std::initializer_list<std::pair<const char *, std::size_t>> list) {
30 for (
auto pair : list) {
35 for (
const auto &pair : list) {
36 result.append(pair.first, pair.first + pair.second);
38 assert(result.size() == size);
42 std::string concatPathPieces(
43 std::initializer_list<std::pair<const char *, std::size_t>> list) {
49 bool firstPieceIsSlash =
false;
51 for (
const auto &pair : list) {
53 if (pair.first[pair.second - 1] ==
'/') {
54 firstPieceIsSlash =
true;
62 if (list.size() > 1 && firstPieceIsSlash) {
68 for (
auto pair : list) {
71 }
else if (firstPieceIsSlash) {
72 firstPieceIsSlash =
false;
77 result.append(pair.first, pair.first + pair.second);
79 assert(result.size() == size);
86 constexpr std::array<char, std::numeric_limits<uint8_t>::max()> escapeMap =
88 std::array<char, std::numeric_limits<uint8_t>::max()> table{};
100 constexpr std::array<char, std::numeric_limits<uint8_t>::max()> unescapeMap =
102 std::array<char, std::numeric_limits<uint8_t>::max()> table{};
116 FCITXUTILS_DEPRECATED_EXPORT
bool startsWith(
const std::string &str,
117 const std::string &prefix) {
118 return str.starts_with(prefix);
121 bool startsWith(std::string_view str, std::string_view prefix) {
122 return str.starts_with(prefix);
125 FCITXUTILS_DEPRECATED_EXPORT
bool endsWith(
const std::string &str,
126 const std::string &suffix) {
127 return str.ends_with(suffix);
130 bool endsWith(std::string_view str, std::string_view suffix) {
131 return str.ends_with(suffix);
134 inline std::pair<std::string::size_type, std::string::size_type>
135 trimInplaceImpl(std::string_view str) {
136 auto start = str.find_first_not_of(FCITX_WHITESPACE);
137 if (start == std::string::npos) {
138 return {str.size(), str.size()};
141 auto end = str.size();
142 while (end > start && charutils::isspace(str[end - 1])) {
149 FCITXUTILS_DEPRECATED_EXPORT
150 std::pair<std::string::size_type, std::string::size_type>
152 return trimInplaceImpl(str);
155 std::pair<std::string::size_type, std::string::size_type>
157 return trimInplaceImpl(str);
160 FCITXUTILS_DEPRECATED_EXPORT
161 std::string
trim(
const std::string &str) {
return trim(std::string_view(str)); }
163 std::string
trim(std::string_view str) {
164 auto pair = trimInplaceImpl(str);
165 return {str.begin() + pair.first, str.begin() + pair.second};
170 return str.substr(pair.first, pair.second - pair.first);
173 FCITXUTILS_DEPRECATED_EXPORT
174 std::vector<std::string>
split(
const std::string &str,
const std::string &delim,
175 SplitBehavior behavior) {
176 return split(std::string_view(str), std::string_view(delim), behavior);
179 std::vector<std::string>
split(std::string_view str, std::string_view delim,
180 SplitBehavior behavior) {
181 std::vector<std::string> strings;
182 std::string::size_type lastPos;
183 std::string::size_type pos;
184 if (behavior == SplitBehavior::SkipEmpty) {
185 lastPos = str.find_first_not_of(delim, 0);
189 pos = str.find_first_of(delim, lastPos);
191 while (std::string::npos != pos || std::string::npos != lastPos) {
192 strings.push_back(std::string(str.substr(lastPos, pos - lastPos)));
193 if (behavior == SplitBehavior::SkipEmpty) {
194 lastPos = str.find_first_not_of(delim, pos);
196 if (pos == std::string::npos) {
201 pos = str.find_first_of(delim, lastPos);
207 FCITXUTILS_DEPRECATED_EXPORT std::vector<std::string>
208 split(
const std::string &str,
const std::string &delim) {
209 return split(std::string_view(str), std::string_view(delim));
212 std::vector<std::string>
split(std::string_view str, std::string_view delim) {
213 return split(str, delim, SplitBehavior::SkipEmpty);
216 std::string
replaceAll(std::string str,
const std::string &before,
217 const std::string &after) {
218 if (before.empty()) {
222 constexpr
int MAX_REPLACE_INDICES_NUM = 128;
225 std::string newString;
227 size_t indices[MAX_REPLACE_INDICES_NUM];
229 size_t newStringPos = 0;
230 size_t oldStringPos = 0;
232 auto copyAndMoveOn = [&newString, &newStringPos](std::string_view source,
239 newString.replace(newStringPos,
length, source, pos,
length);
246 while (nIndices < MAX_REPLACE_INDICES_NUM) {
247 pivot = str.find(before, pivot);
248 if (pivot == std::string::npos) {
252 indices[nIndices++] = pivot;
253 pivot += before.size();
258 lastLen = str.size() + nIndices * after.size() -
259 nIndices * before.size();
260 newString.resize(lastLen);
262 size_t newLen = lastLen + nIndices * after.size() -
263 nIndices * before.size();
265 newString.resize(newLen);
272 copyAndMoveOn(str, oldStringPos, indices[0] - oldStringPos);
273 copyAndMoveOn(after, 0, after.size());
275 for (
int i = 1; i < nIndices; i++) {
276 copyAndMoveOn(str, indices[i - 1] + before.size(),
277 indices[i] - (indices[i - 1] + before.size()));
278 copyAndMoveOn(after, 0, after.size());
281 oldStringPos = indices[nIndices - 1] + before.size();
283 }
while (pivot != std::string::npos);
289 copyAndMoveOn(str, oldStringPos, str.size() - oldStringPos);
290 newString.resize(newStringPos);
296 if (ol_minus_1 < sizeof(unsigned int) * CHAR_BIT) \ 297 hashHaystack -= (a) << ol_minus_1; \ 301 size_t ol,
size_t from) {
305 size_t delta = l - ol;
313 const char *end = haystack;
315 const unsigned int ol_minus_1 = ol - 1;
316 const char *n = needle + ol_minus_1;
317 const char *h = haystack + ol_minus_1;
318 unsigned int hashNeedle = 0;
319 unsigned int hashHaystack = 0;
321 for (idx = 0; idx < ol; ++idx) {
322 hashNeedle = ((hashNeedle << 1) + *(n - idx));
323 hashHaystack = ((hashHaystack << 1) + *(h - idx));
325 hashHaystack -= *haystack;
326 while (haystack >= end) {
327 hashHaystack += *haystack;
328 if (hashHaystack == hashNeedle && memcmp(needle, haystack, ol) == 0) {
332 REHASH(*(haystack + ol));
340 static_cast<const char *>(haystack), l, needle, ol, from));
345 const auto *cstr = haystack.c_str();
346 const auto *result =
backwardSearch(cstr, haystack.size(), needle.c_str(),
347 needle.size(), from);
349 return result - cstr;
351 return std::string::npos;
354 enum class UnescapeState { NORMAL, ESCAPE };
356 bool unescape(std::string &str,
bool unescapeQuote) {
363 UnescapeState state = UnescapeState::NORMAL;
366 case UnescapeState::NORMAL:
367 if (str[i] ==
'\\') {
368 state = UnescapeState::ESCAPE;
374 case UnescapeState::ESCAPE:
375 if (
auto c = unescapeMap[str[i]];
376 c && (unescapeQuote || c !=
'"')) {
379 state = UnescapeState::NORMAL;
392 if (str.size() >= 2 && str.front() ==
'"' && str.back() ==
'"') {
394 auto originLength = str.size();
396 if (consumed.size() == originLength) {
401 return std::string{str};
406 value.reserve(str.size());
407 const bool needEscape =
408 str.find_first_of(
"\f\r\t\v \"\\\n") != std::string::npos;
410 value.push_back(
'"');
413 if (
auto escape = escapeMap[static_cast<uint8_t>(c)]) {
414 value.push_back(
'\\');
415 value.push_back(escape);
421 value.push_back(
'"');
428 if (str.starts_with(prefix)) {
429 str = str.substr(prefix.size());
436 std::string_view skip,
437 std::string *output) {
438 auto start = input.find_first_not_of(skip);
439 if (start == std::string_view::npos) {
440 input = std::string_view();
447 input = input.substr(start);
448 assert(!input.empty());
449 const bool maybeQuoted = input.front() ==
'"';
452 UnescapeState state = UnescapeState::NORMAL;
454 for (
size_t i = 1; i < input.size(); i++) {
455 const char c = input[i];
457 case UnescapeState::NORMAL:
459 state = UnescapeState::ESCAPE;
460 }
else if (c ==
'"') {
467 case UnescapeState::ESCAPE:
468 if (
auto c = unescapeMap[input[i]]) {
473 result.push_back(input[i]);
475 state = UnescapeState::NORMAL;
483 auto consumed = input.substr(0, end);
484 input = input.substr(end);
486 *output = std::move(result);
491 auto end = input.find_first_of(skip, 1);
492 auto consumed = input.substr(0, end);
494 end == std::string_view::npos ? std::string_view() : input.substr(end);
496 *output = std::string(consumed);
std::vector< std::string > split(std::string_view str, std::string_view delim)
Split the string by delim.
std::optional< std::string > unescapeForValue(std::string_view str)
unescape a string if it is quoted, otherwise return the original string.
bool unescape(std::string &str, bool unescapeQuote)
Inplace unescape a string contains slash, new line, optionally quote.
bool consumePrefix(std::string_view &str, std::string_view prefix)
Return a substring of input str if str starts with given prefix.
size_t length(Iter start, Iter end)
Return the number UTF-8 characters in the string iterator range.
std::pair< std::string::size_type, std::string::size_type > trimInplace(std::string_view str)
Trim the whitespace by returning start end end of first and list non whitespace character position...
std::string_view consumeMaybeEscapedValue(std::string_view &input, std::string_view skip, std::string *output)
Consume a value that is potentially quoted and escaped.
std::string_view trimView(std::string_view str)
Trim the white space in string view.
bool endsWith(std::string_view str, std::string_view suffix)
Check if a string ends with a suffix.
std::string replaceAll(std::string str, const std::string &before, const std::string &after)
Replace all substring appearance of before with after.
bool startsWith(std::string_view str, std::string_view prefix)
Check if a string starts with a prefix.
std::string escapeForValue(std::string_view str)
escape a string if str contains certain characters.
size_t backwardSearch(const std::string &haystack, const std::string &needle, size_t from)
Fast backward substring search.
std::string trim(std::string_view str)
Trim the white space in str.
Local independent API to detect character type.