#include "base/include/string/string_utils.h"
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <string>
#include "base/include/fml/macros.h"
namespace lynx {
namespace base {
const char kWhitespaceASCII[] = {0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x20,
0};
const char* SplitString(
std::string_view target, char separator, bool trim,
const std::function<bool(const char*, size_t, int)>& callback) {
if (target.empty()) {
return target.data();
}
auto is_trimmed = [](char c) -> bool { return (c == ' ' || c == '\t'); };
int index = 0;
const char* s = target.data();
const char* end = &target.back() + 1;
if (trim) {
while (s != end && is_trimmed(*s)) {
++s;
}
}
const char* e = s;
while (e != end) {
e = s;
while (e != end && *e != separator) {
++e;
}
if (e - s > 0) {
long sc = 0;
if (trim) {
const char* b = e - 1;
while (b >= s && is_trimmed(*b)) {
--b;
sc++;
}
}
if (e - s - sc > 0) {
if (!callback(s, e - s - sc, index++)) {
return e;
}
}
}
if (e == end) {
break;
}
s = e + 1;
if (trim) {
while (s != end && is_trimmed(*s)) {
++s;
}
}
}
return e;
}
bool SplitString(std::string_view target, char separator,
std::vector<std::string>& result) {
size_t i = 0, len = target.length(), start = i;
while (i < len) {
bool is_last = i == len - 1;
char current_char = target[i];
if (current_char != separator) {
if (is_last) {
result.push_back(std::string(target.substr(start)));
}
} else {
if (i == start) {
start++;
} else {
result.push_back(std::string(target.substr(start, i - start)));
start = i + 1;
}
}
i++;
}
return !result.empty();
}
namespace {
template <class VECTOR_LIKE>
bool SplitStringBySpaceOutOfBrackets(std::string_view target,
VECTOR_LIKE& result) {
size_t i = 0, len = target.length(), start = i, bracket = 0;
while (i < len) {
bool is_last = i == len - 1;
char current_char = target[i];
if (current_char == '(') {
++bracket;
} else if (current_char == ')') {
--bracket;
}
if (bracket > 0 || !isspace(current_char)) {
if (is_last) {
result.push_back(std::string(target.substr(start)));
}
} else {
if (i == start) {
start++;
} else {
result.push_back(std::string(target.substr(start, i - start)));
start = i + 1;
}
}
i++;
}
return !result.empty();
}
}
bool SplitStringBySpaceOutOfBrackets(std::string_view target,
std::vector<std::string>& result) {
return SplitStringBySpaceOutOfBrackets<decltype(result)>(target, result);
}
bool SplitStringBySpaceOutOfBrackets(
std::string_view target, base::InlineVector<std::string, 4>& result) {
return SplitStringBySpaceOutOfBrackets<decltype(result)>(target, result);
}
std::vector<std::string_view> SplitToStringViews(std::string_view str,
const std::string& delims) {
std::vector<std::string_view> output;
for (auto first = str.data(), second = str.data(), last = first + str.size();
second != last && first != last; first = second + 1) {
second =
std::find_first_of(first, last, std::cbegin(delims), std::cend(delims));
if (first != second) {
output.emplace_back(first, second - first);
}
}
return output;
}
std::string Join(const std::vector<std::string>& vec, const char* delim) {
std::stringstream res;
for (size_t i = 0; i < vec.size(); ++i) {
res << vec[i];
if (i < vec.size() - 1) {
res << delim;
}
}
return res.str();
}
std::string JoinString(const std::vector<std::string>& pieces) {
std::string joined;
for (const auto& piece : pieces) {
joined += piece + " ";
}
return joined;
}
bool EndsWithIgnoreSourceCase(std::string_view s, std::string_view ending) {
return EndsWith(StringToLowerASCII(s), ending);
}
std::string CamelCaseToDashCase(std::string_view camel_case_property) {
std::string dash_case_property;
dash_case_property.reserve(camel_case_property.length() * 2);
for (const auto& c : camel_case_property) {
if (c >= 'A' && c <= 'Z') {
dash_case_property.push_back('-');
dash_case_property.push_back(std::tolower(c));
} else {
dash_case_property.push_back(c);
}
}
return dash_case_property;
}
void TrimWhitespaceASCII(std::string_view input, int position,
std::string* output) {
size_t first_good_char = std::string::npos;
for (char c : kWhitespaceASCII) {
size_t pos = input.find_first_not_of(c, position);
if (pos == std::string::npos) continue;
if (first_good_char == std::string::npos) first_good_char = pos;
first_good_char = first_good_char < pos ? pos : first_good_char;
}
size_t last_good_char = input.size();
for (char c : kWhitespaceASCII) {
size_t pos = input.find_last_not_of(c, position);
if (pos == std::string::npos) continue;
last_good_char = last_good_char > pos ? last_good_char : pos;
}
if (input.empty() || first_good_char == std::string::npos ||
last_good_char == std::string::npos) {
output->clear();
return;
}
*output = input.substr(first_good_char, last_good_char - first_good_char + 1);
return;
}
std::string StringToLowerASCII(std::string_view input) {
std::string output;
output.reserve(input.size());
for (char i : input) {
if (i >= 'A' && i <= 'Z') {
output.push_back(i - ('A' - 'a'));
} else {
output.push_back(i);
}
}
return output;
}
std::string TrimString(std::string_view str) {
if (str.empty()) return "";
size_t length = str.size();
uint32_t front_space_count = 0;
uint32_t back_space_count = 0;
uint32_t total_space_count = 0;
while (front_space_count < length && str[front_space_count] == ' ') {
front_space_count++;
}
while (front_space_count + back_space_count < length &&
str[length - back_space_count - 1] == ' ') {
back_space_count++;
}
total_space_count = front_space_count + back_space_count;
return std::string(str.substr(front_space_count, length - total_space_count));
}
std::string_view TrimToStringView(std::string_view to_trim) {
auto left = to_trim.begin();
for (;; ++left) {
if (left == to_trim.end()) {
return std::string_view();
}
if (!std::isspace(*left)) {
break;
}
}
auto right = to_trim.end() - 1;
for (; right > left && isspace(*right); --right) {
}
return std::string_view(to_trim.data() + std::distance(to_trim.begin(), left),
std::distance(left, right) + 1);
}
template <typename T, typename CharT = typename T::value_type>
T TrimStringT(T input, T trim_chars, TrimPositions positions) {
size_t begin =
(positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
size_t end = (positions & TRIM_TRAILING)
? input.find_last_not_of(trim_chars) + 1
: input.size();
return input.substr(std::min(begin, input.size()), end - begin);
}
std::string TrimString(std::string input, std::string trim_chars,
TrimPositions positions) {
return TrimStringT(input, trim_chars, positions);
}
std::string_view TrimString(std::string_view input, std::string_view trim_chars,
TrimPositions positions) {
return TrimStringT(input, trim_chars, positions);
}
base::InlineVector<std::string, 32> SplitStringByCharsOrderly(
std::string_view str, char cs[], size_t char_count) {
const char* byte_array = str.data();
const size_t size = str.size();
base::InlineVector<std::string, 32> result;
if (!size || char_count == 0) {
result.push_back(std::string(str));
return result;
}
char characters[256];
memset(&characters[0], 0, 256);
for (size_t i = 0; i < char_count; i++) {
char c = cs[i];
switch (c) {
case '{':
case '}':
case '(':
case ')':
case '\"':
case '\'': {
return result;
}
default:
break;
}
characters[static_cast<int>(c)] = 1;
}
std::string value;
int word_start = -1;
uint32_t word_count = 0;
bool word_produced = false;
int order = 0;
base::InlineVector<std::string, 8> grouper;
bool is_variable = false;
bool is_string = false;
int end_char = -1;
for (int i = 0; static_cast<size_t>(i) < size; ++i) {
char c = byte_array[i];
if (!is_variable && !is_string && cs[order % char_count] == c) {
word_produced = true;
order++;
} else if (!is_variable && !is_string && characters[static_cast<int>(c)]) {
order = 0;
word_start = -1;
word_count = 0;
grouper.clear();
} else {
if (c == '{') {
is_variable = true;
} else if (is_variable && c == '}') {
is_variable = false;
}
if ((c == '\'' || c == '\"' || c == '(') && !is_string) {
is_string = true;
if (c == '(') {
end_char = ')';
} else {
end_char = c;
}
} else if (is_string && c == end_char) {
is_string = false;
end_char = -1;
}
word_start = word_start == -1 ? i : word_start;
word_count++;
}
if (word_produced || (static_cast<size_t>(i) == size - 1 && word_count)) {
if (word_count && word_start >= 0 &&
static_cast<size_t>(word_start) + static_cast<size_t>(word_count) <=
size) {
grouper.emplace_back(str, static_cast<size_t>(word_start),
static_cast<size_t>(word_count));
} else {
grouper.emplace_back();
}
word_start = -1;
word_count = 0;
if (grouper.size() == char_count) {
for (auto& s : grouper) {
result.emplace_back(std::move(s));
}
}
if (order % char_count == 0) {
grouper.clear();
}
word_produced = false;
}
}
return result;
}
void ReplaceAll(std::string& subject, std::string_view search,
std::string_view replace) {
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
}
std::string SafeStringConvert(const char* str) {
return str == nullptr ? std::string() : str;
}
std::string PtrToStr(void* ptr) {
char temp[20]{0};
#ifndef __EMSCRIPTEN__
std::snprintf(temp, sizeof(temp), "0x%" PRIxPTR,
reinterpret_cast<std::uintptr_t>(ptr));
#else
std::snprintf(temp, sizeof(temp), "0x%zx",
reinterpret_cast<std::uintptr_t>(ptr));
#endif
return temp;
}
bool ConvertParenthesesStringToVector(std::string& origin,
std::vector<std::string>& ret,
char separator) {
origin.erase(remove_if(origin.begin(), origin.end(), isspace), origin.end());
auto start = origin.find("(");
auto end = origin.find(")");
if (start >= end) {
return false;
}
origin = origin.substr(start + 1, end - start - 1);
return base::SplitString(origin, separator, ret);
}
std::vector<std::string> SplitStringIgnoreBracket(std::string str,
char delimiter) {
int start = 0;
std::vector<std::string> result;
bool has_bracket = false;
for (int i = 0; static_cast<size_t>(i) < str.size(); i++) {
if (str[i] == delimiter) {
if (has_bracket) {
continue;
} else {
if (i > start) {
result.push_back(TrimString(str.substr(start, i - start)));
}
start = i + 1;
}
} else if (str[i] == '(') {
has_bracket = true;
} else if (str[i] == ')') {
has_bracket = false;
}
}
if (static_cast<size_t>(start) < str.size()) {
result.push_back(TrimString(str.substr(start, str.size() - start)));
}
return result;
}
bool BothAreSpaces(char lhs, char rhs) {
return (lhs == rhs) && (isspace(lhs));
}
std::string RemoveSpaces(std::string_view str) {
std::stringstream ss;
for (size_t i = 0; i < str.length(); ++i) {
if (!std::isspace(str[i])) {
ss << str[i];
}
}
return ss.str();
}
void ReplaceMultiSpaceWithOne(std::string& str) {
std::string::iterator new_end =
std::unique(str.begin(), str.end(), &BothAreSpaces);
str.erase(new_end, str.end());
}
void ReplaceEscapeCharacterWithLiteralString(std::string& input) {
int newline_count = 0;
int double_quotes_count = 0;
bool pre_is_escape = false;
for (auto c : input) {
if (c == '\\') {
pre_is_escape = true;
continue;
}
if (!pre_is_escape && c == '\"') {
++double_quotes_count;
}
if (c == '\r' || c == '\n' || c == '\t') {
if (double_quotes_count % 2 == 1) {
++newline_count;
}
}
pre_is_escape = false;
}
size_t len = input.size();
input.resize(len + newline_count);
size_t left = len - 1;
size_t right = input.size() - 1;
auto insert_back_slash = [](std::string& str, size_t& index) {
--index;
str[index] = '\\';
};
double_quotes_count = 0;
while (left < right) {
if (input[left] == '\"') {
if (left == 0 || input[left - 1] != '\\') {
++double_quotes_count;
}
}
if (double_quotes_count % 2 == 0) {
input[right] = input[left];
--right;
--left;
continue;
}
if (input[left] == '\n') {
input[right] = 'n';
insert_back_slash(input, right);
} else if (input[left] == '\r') {
input[right] = 'r';
insert_back_slash(input, right);
} else if (input[left] == '\t') {
input[right] = 't';
insert_back_slash(input, right);
} else {
input[right] = input[left];
}
--right;
--left;
}
}
std::u16string U8StringToU16(std::string_view u8_string) {
std::u32string u32str = U8StringToU32(u8_string);
return U32StringToU16(u32str);
}
std::string U16StringToU8(std::u16string_view u16_string) {
std::u32string u32_string = U16StringToU32(u16_string);
return U32StringToU8(u32_string);
}
std::u32string U8StringToU32(std::string_view u8_string) {
size_t length = u8_string.length();
std::u32string u32str;
uint32_t i = 0;
while (i < length) {
uint32_t utf32 = static_cast<unsigned char>(u8_string[i]);
int additional_bytes = 0;
if ((utf32 & 0x80u) == 0) {
additional_bytes = 0;
} else if ((utf32 & 0xE0u) == 0xC0) {
utf32 &= 0x1Fu;
additional_bytes = 1;
} else if ((utf32 & 0xF0u) == 0xE0) {
utf32 &= 0x0Fu;
additional_bytes = 2;
} else if ((utf32 & 0xF8u) == 0xF0) {
utf32 &= 0x07u;
additional_bytes = 3;
} else {
return U"";
}
for (int j = 0; j < additional_bytes; ++j) {
if (++i >= length) {
return U"";
}
auto byte = static_cast<unsigned char>(u8_string[i]);
if ((byte & 0xC0u) != 0x80) {
return U"";
}
utf32 = (utf32 << 6u) | (byte & 0x3Fu);
}
u32str.push_back(utf32);
++i;
}
return u32str;
}
std::string U32StringToU8(std::u32string_view u32_string) {
size_t length = u32_string.length();
std::string utf8;
for (uint32_t i = 0; i < length; ++i) {
auto utf32 = u32_string[i];
if (utf32 <= 0x7F) {
utf8.push_back(utf32);
} else if (utf32 <= 0x7FF) {
utf8.push_back(0xC0 | ((utf32 >> 6u) & 0x1Fu));
utf8.push_back(0x80 | (utf32 & 0x3Fu));
} else if (utf32 <= 0xFFFF) {
utf8.push_back(0xE0 | ((utf32 >> 12u) & 0x0Fu));
utf8.push_back(0x80 | ((utf32 >> 6u) & 0x3Fu));
utf8.push_back(0x80 | (utf32 & 0x3Fu));
} else if (utf32 <= 0x10FFFF) {
utf8.push_back(0xF0 | ((utf32 >> 18u) & 0x07u));
utf8.push_back(0x80 | ((utf32 >> 12u) & 0x3Fu));
utf8.push_back(0x80 | ((utf32 >> 6u) & 0x3Fu));
utf8.push_back(0x80 | (utf32 & 0x3Fu));
} else {
return "";
}
}
return utf8;
}
std::u32string U16StringToU32(std::u16string_view u16_string) {
size_t length = u16_string.length();
std::u32string u32str;
for (uint32_t i = 0; i < length;) {
uint32_t utf32 = u16_string[i];
if (utf32 >= 0xD800 && utf32 <= 0xDBFF) {
if (i + 1 < length) {
uint32_t low_surrogate = u16_string[i + 1];
if (low_surrogate >= 0xDC00 && low_surrogate <= 0xDFFF) {
utf32 = ((utf32 - 0xD800) << 10) + (low_surrogate - 0xDC00) + 0x10000;
i += 2;
} else {
i++;
continue;
}
} else {
i++;
continue;
}
} else {
i++;
}
u32str.push_back(utf32);
}
return u32str;
}
std::u16string U32StringToU16(std::u32string_view u32_string) {
size_t length = u32_string.length();
std::u16string u16str;
for (uint32_t i = 0; i < length; ++i) {
uint32_t utf32 = u32_string[i];
if (utf32 <= 0xFFFF) {
u16str.push_back(static_cast<char16_t>(utf32));
} else if (utf32 >= 0x10000 && utf32 <= 0x10FFFF) {
utf32 -= 0x10000;
u16str.push_back(static_cast<char16_t>(0xD800 | ((utf32 >> 10) & 0x3FF)));
u16str.push_back(static_cast<char16_t>(0xDC00 | (utf32 & 0x3FF)));
} else {
return u"";
}
}
return u16str;
}
bool IsValidUtf8(const uint8_t* data, size_t size) {
size_t index = 0;
while (index < size) {
const uint8_t first = data[index];
if (first <= 0x7f) {
++index;
continue;
}
size_t count = 0;
uint32_t code_point = 0;
if ((first & 0xe0) == 0xc0) {
count = 2;
code_point = first & 0x1f;
} else if ((first & 0xf0) == 0xe0) {
count = 3;
code_point = first & 0x0f;
} else if ((first & 0xf8) == 0xf0) {
count = 4;
code_point = first & 0x07;
} else {
return false;
}
if (index + count > size) {
return false;
}
for (size_t offset = 1; offset < count; ++offset) {
if ((data[index + offset] & 0xc0) != 0x80) {
return false;
}
code_point = (code_point << 6) | (data[index + offset] & 0x3f);
}
if ((count == 2 && code_point < 0x80) ||
(count == 3 && code_point < 0x800) ||
(count == 4 && code_point < 0x10000) ||
(code_point >= 0xd800 && code_point <= 0xdfff) ||
code_point > 0x10ffff) {
return false;
}
index += count;
}
return true;
}
std::string FormatStringWithVaList(const char* format, va_list args) {
int length, size = 100;
char* mes = nullptr;
if ((mes = static_cast<char*>(malloc(size * sizeof(char)))) == nullptr) {
return "";
}
while (true) {
va_list copy_args;
va_copy(copy_args, args);
length = vsnprintf(mes, size, format, copy_args);
va_end(copy_args);
if (length > -1 && length < size) break;
size *= 2;
char* clone = static_cast<char*>(realloc(mes, size * sizeof(char)));
if (clone == nullptr) {
break;
} else {
mes = clone;
clone = nullptr;
}
}
std::string message = mes;
free(mes);
mes = nullptr;
return message;
}
std::string FormatString(const char* format, ...) {
std::string error_msg;
va_list args;
va_start(args, format);
error_msg = FormatStringWithVaList(format, args);
va_end(args);
return error_msg;
}
bool EqualsIgnoreCase(std::string_view left, std::string_view right) {
auto left_lower = StringToLowerASCII(left);
auto right_lower = StringToLowerASCII(right);
return left_lower == right_lower;
}
}
}