#include "../include/StringUtils.hpp" #include #include #include namespace QuickMedia { template static void string_split_t(const std::string_view str, const T &delimiter, StringSplitCallback callback_func, bool include_empty) { size_t index = 0; while(index < str.size()) { size_t new_index = str.find(delimiter, index); if(new_index == std::string::npos) new_index = str.size(); if(include_empty || new_index - index > 0) { if(!callback_func(str.data() + index, new_index - index)) break; } if constexpr(std::is_same::value) index = new_index + 1; else index = new_index + delimiter.size(); } } void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty) { string_split_t(std::string_view(str), delimiter, callback_func, include_empty); } void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func, bool include_empty) { string_split_t(std::string_view(str), delimiter, callback_func, include_empty); } void string_split_view(const std::string_view str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty) { string_split_t(str, delimiter, callback_func, include_empty); } void string_split_view(const std::string_view str, char delimiter, StringSplitCallback callback_func, bool include_empty) { string_split_t(str, delimiter, callback_func, include_empty); } size_t string_replace_all(std::string &str, char old_char, char new_char) { size_t num_replaced_substrings = 0; for(char &c : str) { if(c == old_char) { c = new_char; ++num_replaced_substrings; } } return num_replaced_substrings; } size_t string_replace_all(std::string &str, char old_char, const std::string &new_str) { size_t num_replaced_substrings = 0; size_t index = 0; while(index < str.size()) { index = str.find(old_char, index); if(index == std::string::npos) break; str.replace(index, 1, new_str); index += new_str.size(); ++num_replaced_substrings; } return num_replaced_substrings; } size_t string_replace_all(std::string &str, const std::string &old_str, const std::string &new_str) { size_t num_replaced_substrings = 0; size_t index = 0; while(index < str.size()) { index = str.find(old_str, index); if(index == std::string::npos) break; str.replace(index, old_str.size(), new_str); index += new_str.size(); ++num_replaced_substrings; } return num_replaced_substrings; } static bool is_whitespace(char c) { return c == ' ' || c == '\n' || c == '\t' || c == '\v'; } std::string strip(const std::string &str) { if(str.empty()) return str; int start = 0; for(; start < (int)str.size(); ++start) { if(!is_whitespace(str[start])) break; } int end = str.size() - 1; for(; end >= start; --end) { if(!is_whitespace(str[end])) break; } return str.substr(start, end - start + 1); } void strip(const char *str, size_t size, size_t *new_size) { if(size == 0) { *new_size = 0; return; } int start = 0; for(; start < (int)size; ++start) { if(!is_whitespace(str[start])) break; } int end = (int)size - 1; for(; end >= start; --end) { if(!is_whitespace(str[end])) break; } *new_size = end - start + 1; } bool string_starts_with(const std::string &str, const char *sub) { size_t sub_len = strlen(sub); return sub_len == 0 || (str.size() >= sub_len && memcmp(str.c_str(), sub, sub_len) == 0); } bool string_ends_with(const std::string &str, const std::string &ends_with_str) { size_t ends_len = ends_with_str.size(); return ends_len == 0 || (str.size() >= ends_len && memcmp(&str[str.size() - ends_len], ends_with_str.data(), ends_len) == 0); } size_t str_find_case_insensitive(const std::string &str, size_t start_index, const char *substr, size_t substr_len) { if(substr_len == 0) return 0; auto it = std::search(str.begin() + start_index, str.end(), substr, substr + substr_len, [](char c1, char c2) { return to_upper(c1) == to_upper(c2); }); if(it == str.end()) return std::string::npos; return it - str.begin(); } // TODO: Support utf-8 case insensitive find bool string_find_fuzzy_case_insensitive(const std::string &str, const std::string &substr) { if(substr.empty()) return true; if(str.empty()) return false; size_t str_index = 0; bool full_match = true; string_split(substr, ' ', [&str, &str_index, &full_match](const char *str_part, size_t size) { if(size == 0) return true; size_t found_index = str_find_case_insensitive(str, str_index, str_part, size); if(found_index == std::string::npos) { full_match = false; return false; } str_index = found_index + size; return true; }); return full_match; } char to_upper(char c) { if(c >= 'a' && c <= 'z') return c - 32; else return c; } bool strncase_equals(const char *str1, const char *str2, size_t length) { size_t i = 0; for(;;) { if(i == length) return true; ++i; const char c1 = *str1; const char c2 = *str2; if(to_upper(c1) != to_upper(c2)) return false; else if(c1 == '\0') return true; ++str1; ++str2; } } bool strcase_equals(const char *str1, const char *str2) { for(;;) { const char c1 = *str1; const char c2 = *str2; if(to_upper(c1) != to_upper(c2)) return false; else if(c1 == '\0') return true; ++str1; ++str2; } } bool to_num(const char *str, size_t size, int64_t &num) { if(size == 0) return false; size_t i = 0; const bool is_negative = size > 0 && str[0] == '-'; if(is_negative) i = 1; num = 0; for(; i < size; ++i) { const char num_c = str[i] - '0'; if(num_c < 0 || num_c > 9) return false; num = (num * 10) + num_c; } if(is_negative) num = -num; return true; } bool to_num(const char *str, size_t size, int &num) { int64_t num_i64 = 0; const bool res = to_num(str, size, num_i64); num = num_i64; return res; } bool to_num_hex(const char *str, size_t size, int &num) { if(size == 0) return false; size_t i = 0; const bool is_negative = size > 0 && str[0] == '-'; if(is_negative) i = 1; num = 0; for(; i < size; ++i) { const signed char c = str[i]; if(c - '0' <= 9) num = (num << 4) | (c - '0'); else if(c - 'a' <= 'f' - 'a') num = (num << 4) | (10 + (c - 'a')); else if(c - 'A' <= 'F' - 'A') num = (num << 4) | (10 + (c - 'A')); else return false; } if(is_negative) num = -num; return true; } // Returns relative time as a string (approximation) std::string seconds_to_relative_time_str(time_t seconds) { seconds = std::max(0L, seconds); time_t minutes = seconds / 60; time_t hours = minutes / 60; time_t days = hours / 24; time_t months = days / 30; time_t years = days / 365; if(years >= 1) return std::to_string(years) + " year" + (years == 1 ? "" : "s") + " ago"; else if(months >= 1) return std::to_string(months) + " month" + (months == 1 ? "" : "s") + " ago"; else if(days >= 1) return std::to_string(days) + " day" + (days == 1 ? "" : "s") + " ago"; else if(hours >= 1) return std::to_string(hours) + " hour" + (hours == 1 ? "" : "s") + " ago"; else if(minutes >= 1) return std::to_string(minutes) + " minute" + (minutes == 1 ? "" : "s") + " ago"; else return std::to_string(seconds) + " second" + (seconds == 1 ? "" : "s") + " ago"; } std::string seconds_to_duration(int seconds) { seconds = std::max(0, seconds); int minutes = seconds / 60; int hours = minutes / 60; char buffer[32]; if(hours >= 1) { minutes -= (hours * 60); seconds -= (hours * 60 * 60); seconds -= (minutes * 60); snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", hours, minutes, seconds); } else if(minutes >= 1) { seconds -= (minutes * 60); snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes, seconds); } else { snprintf(buffer, sizeof(buffer), "00:%02d", seconds); } return buffer; } std::string number_separate_thousand_commas(const std::string &number) { const int num_commas = ((int)number.size() - 1) / 3; std::string result; result.resize(number.size() + num_commas); int result_index = (int)number.size() + num_commas - 1; int inc = 0; for(int i = (int)number.size() - 1; i >= 0; --i, ++inc) { if(inc > 0 && inc % 3 == 0) { result[result_index] = ','; --result_index; } result[result_index] = number[i]; --result_index; } return result; } bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) { if(alphabet_size == 0) return false; int fd = open("/dev/urandom", O_RDONLY); if(fd == -1) { perror("/dev/urandom"); return false; } if(read(fd, buffer, buffer_size) < buffer_size) { fprintf(stderr, "Failed to read %d bytes from /dev/urandom\n", buffer_size); close(fd); return false; } for(int i = 0; i < buffer_size; ++i) { unsigned char c = *(unsigned char*)&buffer[i]; buffer[i] = alphabet[c % alphabet_size]; } close(fd); return true; } }