From 2a9b32847fe938402e6da006575718c41c3b291a Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 26 Jun 2022 03:38:28 +0200 Subject: [PATCH] Ported a few helper methods from my String implementation in rcpp_framework. --- core/ustring.cpp | 356 ++++++++++++++++++++++++++++++++++++++++++ core/ustring.h | 35 +++++ core/variant_call.cpp | 43 +++++ 3 files changed, 434 insertions(+) diff --git a/core/ustring.cpp b/core/ustring.cpp index 21c0a129a..5aba93ddf 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -1077,6 +1077,22 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { return ret; } +String String::bool_num(bool p_val) { + if (p_val) { + return "1"; + } else { + return "0"; + } +} + +String String::bool_str(bool p_val) { + if (p_val) { + return "true"; + } else { + return "false"; + } +} + String String::chr(CharType p_char) { CharType c[2] = { p_char, 0 }; return String(c); @@ -1857,6 +1873,26 @@ int String::to_int() const { return integer * sign; } +bool String::to_bool() const { + if (length() == 0) { + return false; + } + + if (is_numeric()) { + return to_int() != 0; + } + + return to_lower() == "true"; +} + +uint32_t String::to_uint() const { + if (is_numeric()) { + return static_cast(to_int()); + } + + return 0; +} + int64_t String::to_int64() const { if (length() == 0) { return 0; @@ -1940,6 +1976,40 @@ bool String::is_numeric() const { return true; // TODO: Use the parser below for this instead }; +bool String::is_zero() const { + int size = length(); + + if (size == 0) { + return false; + } + + int starti = 0; + + if (operator[](0) == '-') { + starti += 1; + } + + bool had_dot = false; + for (int i = starti; i < size; ++i) { + CharType c = operator[](i); + + if (c == '.') { + if (!had_dot) { + had_dot = true; + continue; + } else { + return false; + } + } + + if (c != '0') { + return false; + } + } + + return true; +} + template static double built_in_strtod( /* A decimal ASCII floating-point number, @@ -2431,6 +2501,20 @@ String String::substr(int p_from, int p_chars) const { return s; } +String String::substr_index(const int start_index, const int end_index) const { + int s = length(); + + if (start_index < 0 || start_index >= s || end_index < 0 || start_index >= s) { + return ""; + } + + if (start_index > end_index) { + return ""; + } + + return substr(start_index, end_index - start_index); +} + int String::find_last(const String &p_str) const { return rfind(p_str); } @@ -2723,6 +2807,66 @@ int String::rfindn(const String &p_str, int p_from) const { return -1; } +int String::find_first_difference_index(const String &p_str) const { + const int olen = p_str.length(); + const int len = length(); + const int c = len < olen ? len : olen; + + const CharType *p = c_str(); + const CharType *op = p_str.c_str(); + + for (int i = 0; i < c; ++i) { + if (p[i] != op[i]) { + return i; + } + } + + return c; +} + +bool String::is_word_at(const int index, const char *p_str) const { + int size = length(); + + ERR_FAIL_INDEX_V(index, size, false); + + int i = 0; + + while (p_str[i] != '\0') { + int iind = index + i; + + if (iind >= size) { + return false; + } + + if (operator[](iind) != p_str[i]) { + return false; + } + + ++i; + } + + return true; +} +bool String::is_word_at(const int index, const String &p_str) const { + int size = length(); + + ERR_FAIL_INDEX_V(index, size, false); + + if (index + p_str.length() >= size) { + return false; + } + + for (int i = 0; i < p_str.length(); ++i) { + int iind = index + i; + + if (operator[](iind) != p_str[i]) { + return false; + } + } + + return true; +} + bool String::ends_with(const String &p_string) const { int l = p_string.length(); if (l > length()) { @@ -3078,6 +3222,11 @@ String String::replacen(const String &p_key, const String &p_with) const { return new_string; } +String String::newline_to_br() const { + String r = replace("\r\n", "
"); + return r.replace("\n", "
"); +} + String String::repeat(int p_count) const { ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); @@ -3333,6 +3482,156 @@ String String::simplify_path() const { return drive + s; } +String String::append_path(const char *path) const { + if (path[0] == '\0') { + return *this; + } + + String ret = *this; + int size = length(); + + if (size == 0) { + ret += path; + return ret; + } + + int sindex = 0; + char ch = path[sindex]; + while (ch == '/' || ch == '\\') { + if (ch == '\0') { + return ret; + } + + ch = path[++sindex]; + } + + // /////folder + // ^ (sindex) + + if (ret.ends_with("/") || ret.ends_with("\\")) { + ret += &path[sindex]; + } else { + if (sindex > 0) { + ret += '/'; + ret += &path[sindex - 1]; + } else { + ret += '/'; + ret += &path[sindex]; + } + } + + return ret; +} + +String String::append_path(const String &path) const { + if (path.length() == 0) { + return *this; + } + + int size = length(); + + if (size == 0) { + return path; + } + + int sindex = 0; + int ts = path.size() - 1; + char ch = path[sindex]; + while (ch == '/' || ch == '\\') { + if (sindex == ts) { + return *this; + } + + ch = path[++sindex]; + } + + String ret = *this; + + // /////folder + // ^ (sindex) + + if (ret.ends_with("/") || ret.ends_with("\\")) { + ret += &path[sindex]; + } else { + if (sindex > 0) { + ret += '/'; + ret += &path[sindex - 1]; + } else { + ret += '/'; + ret += &path[sindex]; + } + } + + return ret; +} + +String String::path_clean_end_slash() const { + // _size > 1, so if root is given ("/"), it will not be removed + + String ret = *this; + + while (ret.length() > 1 && (ret.ends_with("/") || ret.ends_with("\\"))) { + ret.resize(length() - 1); + } + + return ret; +} +String String::path_ensure_end_slash() const { + // Don't add if empty string, as it would make it root on linux, which can easily become a serious bug + + String ret = *this; + + if (ret.length() == 0) { + return ret; + } + + if (!(ret.ends_with("/") || ret.ends_with("\\"))) { + ret += "/"; + } + + return ret; +} + +String String::path_get_prev_dir() const { + int size = length(); + + if (size == 0) { + return "/"; + } + + int seind = size - 1; + while (seind > 0 && (operator[](seind) == '/' || operator[](seind) == '\\')) { + --seind; + } + + if (seind == 0) { + // ///////// + // or + // a/////// + // no prev dir + + return "/"; + } + + // fol/fol2/fol3// + // ^ (seind) + + while (seind > 0 && (operator[](seind) != '/' && operator[](seind) != '\\')) { + --seind; + } + + // fol/fol2/fol3// + // ^ (seind) + + //--seind; + + if (seind <= 0) { + return "/"; + } + + return substr_index(0, seind); +} + static int _humanize_digits(int p_num) { if (p_num < 100) { return 2; @@ -3845,6 +4144,63 @@ bool String::is_valid_float() const { return numbers_found; } +bool String::is_valid_bool() const { + int size = length(); + + if (size == 1) { + CharType c = ptr()[0]; + + if (c == '0') { + return true; + } else if (c == '1') { + return true; + } + + return false; + } else if (size == 4) { + String l = to_lower(); + const CharType *p = l.ptr(); + + if (p[0] == 't' && p[1] == 'r' && p[2] == 'u' && p[3] == 'e') { + return true; + } else { + return false; + } + } else if (size == 5) { + String l = to_lower(); + const CharType *p = l.ptr(); + + if (p[0] == 'f' && p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[3] == 'e') { + return true; + } else { + return false; + } + } + + return false; +} + +bool String::is_valid_unsigned_integer() const { + int len = length(); + + if (len == 0) { + return false; + } + + int from = 0; + if (len != 1 && (operator[](0) == '+')) { + from++; + } + + for (int i = from; i < len; i++) { + if (operator[](i) < '0' || operator[](i) > '9') { + return false; // no start with number plz + } + } + + return true; +} + String String::path_to_file(const String &p_path) const { // Don't get base dir for src, this is expected to be a dir already. String src = this->replace("\\", "/"); diff --git a/core/ustring.h b/core/ustring.h index d9cf29c81..12ca98157 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -202,6 +202,8 @@ public: /* complex helpers */ String substr(int p_from, int p_chars = -1) const; + String substr_index(const int p_start_index, const int p_end_index) const; //end_index is not included + int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed @@ -210,33 +212,52 @@ public: int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive int findmk(const Vector &p_keys, int p_from = 0, int *r_key = nullptr) const; ///< return <0 if failed + int find_first_difference_index(const String &p_str) const; + + bool is_word_at(const int p_index, const char *p_str) const; + bool is_word_at(const int p_index, const String &p_str) const; + bool match(const String &p_wildcard) const; bool matchn(const String &p_wildcard) const; + bool begins_with(const String &p_string) const; bool begins_with(const char *p_string) const; bool ends_with(const String &p_string) const; + bool is_enclosed_in(const String &p_string) const; bool is_subsequence_of(const String &p_string) const; bool is_subsequence_ofi(const String &p_string) const; bool is_quoted() const; + Vector bigrams() const; float similarity(const String &p_string) const; String format(const Variant &values, String placeholder = "{_}") const; + String replace_first(const String &p_key, const String &p_with) const; String replace(const String &p_key, const String &p_with) const; String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; + + String newline_to_br() const; + String repeat(int p_count) const; String insert(int p_at_pos, const String &p_string) const; + String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; + String trim_prefix(const String &p_prefix) const; String trim_suffix(const String &p_suffix) const; + String lpad(int min_length, const String &character = " ") const; String rpad(int min_length, const String &character = " ") const; + String sprintf(const Array &values, bool *error) const; + String quote(String quotechar = "\"") const; + String unquote() const; + static String num(double p_num, int p_decimals = -1); static String num_scientific(double p_num); static String num_real(double p_num); @@ -245,11 +266,18 @@ public: static String chr(CharType p_char); static String md5(const uint8_t *p_md5); static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); + static String bool_num(bool p_val); + static String bool_str(bool p_val); + bool is_numeric() const; + bool is_zero() const; + double to_double() const; float to_float() const; int hex_to_int(bool p_with_prefix = true) const; int to_int() const; + bool to_bool() const; + uint32_t to_uint() const; int64_t hex_to_int64(bool p_with_prefix = true) const; int64_t bin_to_int64(bool p_with_prefix = true) const; @@ -328,6 +356,11 @@ public: String get_file() const; static String humanize_size(uint64_t p_size); String simplify_path() const; + String append_path(const char *p_path) const; + String append_path(const String &p_path) const; + String path_clean_end_slash() const; + String path_ensure_end_slash() const; + String path_get_prev_dir() const; String xml_escape(bool p_escape_quotes = false) const; String xml_unescape() const; @@ -356,6 +389,8 @@ public: bool is_valid_html_color() const; bool is_valid_ip_address() const; bool is_valid_filename() const; + bool is_valid_bool() const; + bool is_valid_unsigned_integer() const; /** * The constructors must not depend on other overloads diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 81d2efee7..16eee4a39 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -273,12 +273,15 @@ struct _VariantCall { VCALL_LOCALMEM3R(String, count); VCALL_LOCALMEM3R(String, countn); VCALL_LOCALMEM2R(String, substr); + VCALL_LOCALMEM2R(String, substr_index); VCALL_LOCALMEM2R(String, get_slice); VCALL_LOCALMEM2R(String, find); VCALL_LOCALMEM1R(String, find_last); VCALL_LOCALMEM2R(String, findn); VCALL_LOCALMEM2R(String, rfind); VCALL_LOCALMEM2R(String, rfindn); + VCALL_LOCALMEM1R(String, find_first_difference_index); + VCALL_LOCALMEM2R(String, is_word_at); VCALL_LOCALMEM1R(String, match); VCALL_LOCALMEM1R(String, matchn); VCALL_LOCALMEM1R(String, begins_with); @@ -290,6 +293,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(String, format); VCALL_LOCALMEM2R(String, replace); VCALL_LOCALMEM2R(String, replacen); + VCALL_LOCALMEM0R(String, newline_to_br); VCALL_LOCALMEM1R(String, repeat); VCALL_LOCALMEM2R(String, insert); VCALL_LOCALMEM0R(String, capitalize); @@ -326,6 +330,10 @@ struct _VariantCall { VCALL_LOCALMEM0R(String, is_rel_path); VCALL_LOCALMEM0R(String, get_base_dir); VCALL_LOCALMEM0R(String, get_file); + VCALL_LOCALMEM1R(String, append_path); + VCALL_LOCALMEM0R(String, path_clean_end_slash); + VCALL_LOCALMEM0R(String, path_ensure_end_slash); + VCALL_LOCALMEM0R(String, path_get_prev_dir); VCALL_LOCALMEM0R(String, xml_escape); VCALL_LOCALMEM0R(String, xml_unescape); VCALL_LOCALMEM0R(String, http_escape); @@ -343,8 +351,14 @@ struct _VariantCall { VCALL_LOCALMEM0R(String, is_valid_html_color); VCALL_LOCALMEM0R(String, is_valid_ip_address); VCALL_LOCALMEM0R(String, is_valid_filename); + VCALL_LOCALMEM0R(String, is_valid_bool); + VCALL_LOCALMEM0R(String, is_valid_unsigned_integer); + VCALL_LOCALMEM0R(String, is_numeric); + VCALL_LOCALMEM0R(String, is_zero); VCALL_LOCALMEM0RI(String, to_int, to_int64); VCALL_LOCALMEM0R(String, to_float); + VCALL_LOCALMEM0R(String, to_bool); + VCALL_LOCALMEM0R(String, to_uint); VCALL_LOCALMEM0RI(String, hex_to_int, hex_to_int64); VCALL_LOCALMEM1R(String, pad_decimals); VCALL_LOCALMEM1R(String, pad_zeros); @@ -1873,7 +1887,10 @@ void register_variant_methods() { ADDFUNC1R(STRING, INT, String, nocasecmp_to, STRING, "to", varray()); ADDFUNC1R(STRING, INT, String, naturalnocasecmp_to, STRING, "to", varray()); ADDFUNC0R(STRING, INT, String, length, varray()); + ADDFUNC2R(STRING, STRING, String, substr, INT, "from", INT, "len", varray(-1)); + ADDFUNC2R(STRING, STRING, String, substr_index, INT, "start_index", INT, "end_index", varray()); + ADDFUNC2R(STRING, STRING, String, get_slice, STRING, "delimiter", INT, "slice", varray()); ADDFUNC2R(STRING, INT, String, find, STRING, "what", INT, "from", varray(0)); @@ -1885,6 +1902,11 @@ void register_variant_methods() { ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0)); ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1)); ADDFUNC2R(STRING, INT, String, rfindn, STRING, "what", INT, "from", varray(-1)); + + ADDFUNC1R(STRING, INT, String, find_first_difference_index, STRING, "what", varray(0)); + + ADDFUNC2R(STRING, INT, String, is_word_at, INT, "index", STRING, "str", varray()); + ADDFUNC1R(STRING, BOOL, String, match, STRING, "expr", varray()); ADDFUNC1R(STRING, BOOL, String, matchn, STRING, "expr", varray()); ADDFUNC1R(STRING, BOOL, String, begins_with, STRING, "text", varray()); @@ -1897,6 +1919,9 @@ void register_variant_methods() { ADDFUNC2R(STRING, STRING, String, format, NIL, "values", STRING, "placeholder", varray("{_}")); ADDFUNC2R(STRING, STRING, String, replace, STRING, "what", STRING, "forwhat", varray()); ADDFUNC2R(STRING, STRING, String, replacen, STRING, "what", STRING, "forwhat", varray()); + + ADDFUNC0R(STRING, STRING, String, newline_to_br, varray()); + ADDFUNC1R(STRING, STRING, String, repeat, INT, "count", varray()); ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); ADDFUNC0R(STRING, STRING, String, capitalize, varray()); @@ -1921,6 +1946,7 @@ void register_variant_methods() { ADDFUNC1R(STRING, STRING, String, indent, STRING, "prefix", varray()); ADDFUNC0R(STRING, STRING, String, dedent, varray()); ADDFUNC2(STRING, NIL, String, erase, INT, "position", INT, "chars", varray()); + ADDFUNC0R(STRING, INT, String, hash, varray()); ADDFUNC0R(STRING, STRING, String, md5_text, varray()); ADDFUNC0R(STRING, STRING, String, sha1_text, varray()); @@ -1928,13 +1954,20 @@ void register_variant_methods() { ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, md5_buffer, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, sha1_buffer, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, sha256_buffer, varray()); + ADDFUNC0R(STRING, BOOL, String, empty, varray()); ADDFUNC1R(STRING, STRING, String, humanize_size, INT, "size", varray()); + ADDFUNC0R(STRING, BOOL, String, is_abs_path, varray()); ADDFUNC0R(STRING, STRING, String, simplify_path, varray()); ADDFUNC0R(STRING, BOOL, String, is_rel_path, varray()); ADDFUNC0R(STRING, STRING, String, get_base_dir, varray()); ADDFUNC0R(STRING, STRING, String, get_file, varray()); + ADDFUNC1R(STRING, STRING, String, append_path, STRING, "path", varray()); + ADDFUNC0R(STRING, STRING, String, path_clean_end_slash, varray()); + ADDFUNC0R(STRING, STRING, String, path_ensure_end_slash, varray()); + ADDFUNC0R(STRING, STRING, String, path_get_prev_dir, varray()); + ADDFUNC0R(STRING, STRING, String, xml_escape, varray()); ADDFUNC0R(STRING, STRING, String, xml_unescape, varray()); ADDFUNC0R(STRING, STRING, String, http_escape, varray()); @@ -1945,6 +1978,7 @@ void register_variant_methods() { ADDFUNC0R(STRING, STRING, String, percent_encode, varray()); ADDFUNC0R(STRING, STRING, String, percent_decode, varray()); ADDFUNC0R(STRING, STRING, String, validate_node_name, varray()); + ADDFUNC0R(STRING, BOOL, String, is_valid_identifier, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_integer, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_float, varray()); @@ -1952,8 +1986,17 @@ void register_variant_methods() { ADDFUNC0R(STRING, BOOL, String, is_valid_html_color, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_ip_address, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_filename, varray()); + ADDFUNC0R(STRING, BOOL, String, is_valid_bool, varray()); + ADDFUNC0R(STRING, BOOL, String, is_valid_unsigned_integer, varray()); + + ADDFUNC0R(STRING, BOOL, String, is_numeric, varray()); + ADDFUNC0R(STRING, BOOL, String, is_zero, varray()); + ADDFUNC0R(STRING, INT, String, to_int, varray()); ADDFUNC0R(STRING, REAL, String, to_float, varray()); + ADDFUNC0R(STRING, INT, String, to_bool, varray()); + ADDFUNC0R(STRING, INT, String, to_uint, varray()); + ADDFUNC0R(STRING, INT, String, hex_to_int, varray()); ADDFUNC1R(STRING, STRING, String, pad_decimals, INT, "digits", varray()); ADDFUNC1R(STRING, STRING, String, pad_zeros, INT, "digits", varray());