From 80b8eff6aa41ba79175a5152ba5b2b9b16f6de3f Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 27 Jul 2020 13:43:20 +0300 Subject: [PATCH] [Complex Test Layouts] Change `String` to use UTF-32 encoding on all platforms. --- core/color.cpp | 4 +- core/cowdata.h | 2 + core/hashfuncs.h | 2 + core/io/file_access_pack.cpp | 2 +- core/io/file_access_zip.cpp | 4 +- core/io/ip_address.cpp | 4 +- core/io/json.cpp | 20 +- core/io/json.h | 8 +- core/io/xml_parser.cpp | 4 +- core/math/expression.cpp | 20 +- core/method_bind.h | 14 +- core/os/file_access.cpp | 8 +- core/string_buffer.h | 24 +- core/string_builder.cpp | 4 +- core/string_name.cpp | 2 +- core/string_name.h | 2 +- core/type_info.h | 3 +- core/ustring.cpp | 1140 +++++++--- core/ustring.h | 178 +- core/variant.cpp | 4 +- core/variant.h | 4 +- core/variant_call.cpp | 63 + core/variant_op.cpp | 2 +- core/variant_parser.cpp | 30 +- core/variant_parser.h | 8 +- doc/classes/PackedByteArray.xml | 18 +- doc/classes/String.xml | 49 +- drivers/unix/file_access_unix.cpp | 2 +- drivers/vulkan/rendering_device_vulkan.cpp | 20 +- drivers/windows/dir_access_windows.cpp | 132 +- drivers/windows/file_access_windows.cpp | 57 +- editor/connections_dialog.cpp | 2 +- editor/editor_help.cpp | 16 +- editor/editor_run.cpp | 4 +- editor/find_in_files.cpp | 4 +- editor/input_map_editor.cpp | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 2 +- editor/plugins/editor_preview_plugins.cpp | 4 +- editor/plugins/node_3d_editor_plugin.cpp | 2 +- modules/gdnative/gdnative/string.cpp | 691 +++--- modules/gdnative/gdnative_api.json | 398 +++- modules/gdnative/include/gdnative/string.h | 152 +- .../gdnative/nativescript/api_generator.cpp | 4 +- .../gdscript/editor/gdscript_highlighter.cpp | 10 +- modules/gdscript/gdscript_functions.cpp | 2 +- modules/gdscript/gdscript_parser.cpp | 2 +- modules/gdscript/gdscript_tokenizer.cpp | 46 +- modules/gdscript/gdscript_tokenizer.h | 22 +- .../gdscript_extend_parser.cpp | 6 +- modules/mono/editor/script_class_parser.cpp | 8 +- modules/mono/mono_gd/gd_mono_marshal.cpp | 38 - modules/mono/mono_gd/gd_mono_marshal.h | 26 +- modules/mono/utils/mono_reg_utils.cpp | 8 +- modules/mono/utils/path_utils.cpp | 4 +- modules/mono/utils/string_utils.cpp | 6 +- modules/regex/regex.cpp | 233 +- .../visual_script_builtin_funcs.cpp | 2 +- .../visual_script_expression.cpp | 14 +- modules/visual_script/visual_script_nodes.cpp | 52 +- platform/android/export/export.cpp | 8 +- platform/iphone/display_server_iphone.mm | 4 +- platform/iphone/export/export.cpp | 2 +- platform/iphone/in_app_store.mm | 2 +- platform/iphone/ios.mm | 2 +- platform/iphone/os_iphone.mm | 4 +- platform/linuxbsd/crash_handler_linuxbsd.cpp | 4 +- platform/osx/crash_handler_osx.mm | 4 +- platform/osx/export/export.cpp | 2 +- platform/uwp/os_uwp.cpp | 8 +- platform/windows/crash_handler_windows.cpp | 2 +- platform/windows/display_server_windows.cpp | 41 +- platform/windows/os_windows.cpp | 61 +- scene/gui/label.cpp | 12 +- scene/gui/line_edit.cpp | 16 +- scene/gui/rich_text_effect.h | 4 +- scene/gui/rich_text_label.cpp | 6 +- scene/gui/text_edit.cpp | 90 +- scene/gui/text_edit.h | 4 +- scene/main/canvas_item.cpp | 4 +- scene/main/node.cpp | 4 +- scene/resources/dynamic_font.cpp | 16 +- scene/resources/dynamic_font.h | 16 +- scene/resources/font.cpp | 26 +- scene/resources/font.h | 26 +- scene/resources/syntax_highlighter.cpp | 8 +- scene/resources/tile_set.cpp | 4 +- servers/rendering/shader_language.cpp | 30 +- servers/text_server.h | 0 tests/SCsub | 3 + tests/test_gdnative_string.h | 1985 +++++++++++++++++ tests/test_main.cpp | 1 + tests/test_math.cpp | 8 +- tests/test_shader_lang.cpp | 2 +- tests/test_string.h | 572 ++++- 94 files changed, 4889 insertions(+), 1686 deletions(-) create mode 100644 servers/text_server.h create mode 100644 tests/test_gdnative_string.h diff --git a/core/color.cpp b/core/color.cpp index c85cd9100d..c61ee0e64a 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -390,7 +390,7 @@ String _to_hex(float p_val) { String ret; for (int i = 0; i < 2; i++) { - CharType c[2] = { 0, 0 }; + char32_t c[2] = { 0, 0 }; int lv = v & 0xF; if (lv < 10) { c[0] = '0' + lv; @@ -399,7 +399,7 @@ String _to_hex(float p_val) { } v >>= 4; - String cs = (const CharType *)c; + String cs = (const char32_t *)c; ret = cs + ret; } diff --git a/core/cowdata.h b/core/cowdata.h index 82daefb5bd..79676e6d80 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -40,6 +40,7 @@ template class Vector; class String; +class Char16String; class CharString; template class VMap; @@ -49,6 +50,7 @@ class CowData { template friend class Vector; friend class String; + friend class Char16String; friend class CharString; template friend class VMap; diff --git a/core/hashfuncs.h b/core/hashfuncs.h index d984f6c524..f4048843fc 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -146,6 +146,8 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } + static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; } + static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; } static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); } static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 024ec3b2b5..cc8d68be83 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -46,7 +46,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, size_t p_ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files) { PathMD5 pmd5(path.md5_buffer()); - //printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b); + //printf("adding path %s, %lli, %lli\n", path.utf8().get_data(), pmd5.a, pmd5.b); bool exists = files.has(pmd5); diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 974bb65a18..d75ca2fdc6 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -148,7 +148,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const { } bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset = 0) { - //printf("opening zip pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); + //printf("opening zip pack %s, %i, %i\n", p_name.utf8().get_data(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); // load with offset feature only supported for PCK files ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); @@ -201,7 +201,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, size_ uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files); - //printf("packed data add path %ls, %ls\n", p_name.c_str(), fname.c_str()); + //printf("packed data add path %s, %s\n", p_name.utf8().get_data(), fname.utf8().get_data()); if ((i + 1) < gi.number_entry) { unzGoToNextFile(zfile); diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index c7a0ae5605..d0fb63b958 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { } int n = 0; - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c >= '0' && c <= '9') { n = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -101,7 +101,7 @@ void IP_Address::_parse_ipv6(const String &p_string) { int parts_idx = 0; for (int i = 0; i < p_string.length(); i++) { - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c == ':') { if (i == 0) { continue; // next must be a ":" diff --git a/core/io/json.cpp b/core/io/json.cpp index 8bdd6385cb..1b89d966fd 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -125,7 +125,7 @@ String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_key return _print_var(p_var, p_indent, 0, p_sort_keys); } -Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { +Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { while (p_len > 0) { switch (p_str[index]) { case '\n': { @@ -180,12 +180,12 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to } else if (p_str[index] == '\\') { //escaped characters... index++; - CharType next = p_str[index]; + char32_t next = p_str[index]; if (next == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -206,7 +206,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = p_str[index + j + 1]; + char32_t c = p_str[index + j + 1]; if (c == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; @@ -215,7 +215,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -264,7 +264,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { //a number - const CharType *rptr; + const char32_t *rptr; double number = String::to_float(&p_str[index], &rptr); index += (rptr - &p_str[index]); r_token.type = TK_NUMBER; @@ -293,7 +293,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to return ERR_PARSE_ERROR; } -Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { if (token.type == TK_CURLY_BRACKET_OPEN) { Dictionary d; Error err = _parse_object(d, p_str, index, p_len, line, r_err_str); @@ -337,7 +337,7 @@ Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, in } } -Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { Token token; bool need_comma = false; @@ -375,7 +375,7 @@ Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_ return ERR_PARSE_ERROR; } -Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { bool at_key = true; String key; Token token; @@ -439,7 +439,7 @@ Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index, } Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) { - const CharType *str = p_json.ptr(); + const char32_t *str = p_json.ptr(); int idx = 0; int len = p_json.length(); Token token; diff --git a/core/io/json.h b/core/io/json.h index 4fc5630a93..9122228163 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -65,10 +65,10 @@ class JSON { static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys); - static Error _get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); - static Error _parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_object(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); + static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); public: static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true); diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index b11267b60f..fc75ac7d1e 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -36,7 +36,7 @@ VARIANT_ENUM_CAST(XMLParser::NodeType); -static bool _equalsn(const CharType *str1, const CharType *str2, int len) { +static bool _equalsn(const char32_t *str1, const char32_t *str2, int len) { int i; for (i = 0; i < len && str1[i] && str2[i]; ++i) { if (str1[i] != str2[i]) { @@ -64,7 +64,7 @@ String XMLParser::_replace_special_characters(const String &origstr) { int specialChar = -1; for (int i = 0; i < (int)special_characters.size(); ++i) { - const CharType *p = &origstr[pos] + 1; + const char32_t *p = &origstr[pos] + 1; if (_equalsn(&special_characters[i][1], p, special_characters[i].length() - 1)) { specialChar = i; diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 735a30f6cc..1040f9e0e4 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -596,7 +596,7 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant } break; case TEXT_CHAR: { - CharType result[2] = { *p_inputs[0], 0 }; + char32_t result[2] = { *p_inputs[0], 0 }; *r_return = String(result); @@ -739,7 +739,7 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant //////// -static bool _is_number(CharType c) { +static bool _is_number(char32_t c) { return (c >= '0' && c <= '9'); } @@ -747,7 +747,7 @@ Error Expression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) - CharType cchar = GET_CHAR(); + char32_t cchar = GET_CHAR(); switch (cchar) { case 0: { @@ -900,7 +900,7 @@ Error Expression::_get_token(Token &r_token) { case '"': { String str; while (true) { - CharType ch = GET_CHAR(); + char32_t ch = GET_CHAR(); if (ch == 0) { _set_error("Unterminated String"); @@ -912,13 +912,13 @@ Error Expression::_get_token(Token &r_token) { } else if (ch == '\\') { //escaped characters... - CharType next = GET_CHAR(); + char32_t next = GET_CHAR(); if (next == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -939,7 +939,7 @@ Error Expression::_get_token(Token &r_token) { case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = GET_CHAR(); + char32_t c = GET_CHAR(); if (c == 0) { _set_error("Unterminated String"); @@ -951,7 +951,7 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (_is_number(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -992,7 +992,7 @@ Error Expression::_get_token(Token &r_token) { break; } - CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { //a number @@ -1004,7 +1004,7 @@ Error Expression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; diff --git a/core/method_bind.h b/core/method_bind.h index ff2c771f81..942e2e0036 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -181,18 +181,18 @@ VARIANT_ENUM_CAST(Variant::Type); VARIANT_ENUM_CAST(Variant::Operator); template <> -struct VariantCaster { - static _FORCE_INLINE_ wchar_t cast(const Variant &p_variant) { - return (wchar_t)p_variant.operator int(); +struct VariantCaster { + static _FORCE_INLINE_ char32_t cast(const Variant &p_variant) { + return (char32_t)p_variant.operator int(); } }; #ifdef PTRCALL_ENABLED template <> -struct PtrToArg { - _FORCE_INLINE_ static wchar_t convert(const void *p_ptr) { - return wchar_t(*reinterpret_cast(p_ptr)); +struct PtrToArg { + _FORCE_INLINE_ static char32_t convert(const void *p_ptr) { + return char32_t(*reinterpret_cast(p_ptr)); } - _FORCE_INLINE_ static void encode(wchar_t p_val, const void *p_ptr) { + _FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) { *(int *)p_ptr = p_val; } }; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 20b3435911..9dbb2952f7 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -234,7 +234,7 @@ double FileAccess::get_double() const { String FileAccess::get_token() const { CharString token; - CharType c = get_8(); + char32_t c = get_8(); while (!eof_reached()) { if (c <= ' ') { @@ -299,7 +299,7 @@ public: String FileAccess::get_line() const { CharBuffer line; - CharType c = get_8(); + char32_t c = get_8(); while (!eof_reached()) { if (c == '\n' || c == '\0') { @@ -342,8 +342,8 @@ Vector FileAccess::get_csv_line(const String &p_delim) const { bool in_quote = false; String current; for (int i = 0; i < l.length(); i++) { - CharType c = l[i]; - CharType s[2] = { 0, 0 }; + char32_t c = l[i]; + char32_t s[2] = { 0, 0 }; if (!in_quote && c == p_delim[0]) { strings.push_back(current); diff --git a/core/string_buffer.h b/core/string_buffer.h index f9cf31075a..a685720851 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -35,21 +35,21 @@ template class StringBuffer { - CharType short_buffer[SHORT_BUFFER_SIZE]; + char32_t short_buffer[SHORT_BUFFER_SIZE]; String buffer; int string_length = 0; - _FORCE_INLINE_ CharType *current_buffer_ptr() { + _FORCE_INLINE_ char32_t *current_buffer_ptr() { return static_cast(buffer).empty() ? short_buffer : buffer.ptrw(); } public: - StringBuffer &append(CharType p_char); + StringBuffer &append(char32_t p_char); StringBuffer &append(const String &p_string); StringBuffer &append(const char *p_str); - StringBuffer &append(const CharType *p_str, int p_clip_to_len = -1); + StringBuffer &append(const char32_t *p_str, int p_clip_to_len = -1); - _FORCE_INLINE_ void operator+=(CharType p_char) { + _FORCE_INLINE_ void operator+=(char32_t p_char) { append(p_char); } @@ -61,7 +61,7 @@ public: append(p_str); } - _FORCE_INLINE_ void operator+=(const CharType *p_str) { + _FORCE_INLINE_ void operator+=(const char32_t *p_str) { append(p_str); } @@ -80,7 +80,7 @@ public: }; template -StringBuffer &StringBuffer::append(CharType p_char) { +StringBuffer &StringBuffer::append(char32_t p_char) { reserve(string_length + 2); current_buffer_ptr()[string_length++] = p_char; return *this; @@ -88,7 +88,7 @@ StringBuffer &StringBuffer::append(CharTyp template StringBuffer &StringBuffer::append(const String &p_string) { - return append(p_string.c_str()); + return append(p_string.get_data()); } template @@ -96,7 +96,7 @@ StringBuffer &StringBuffer::append(const c int len = strlen(p_str); reserve(string_length + len + 1); - CharType *buf = current_buffer_ptr(); + char32_t *buf = current_buffer_ptr(); for (const char *c_ptr = p_str; *c_ptr; ++c_ptr) { buf[string_length++] = *c_ptr; } @@ -104,13 +104,13 @@ StringBuffer &StringBuffer::append(const c } template -StringBuffer &StringBuffer::append(const CharType *p_str, int p_clip_to_len) { +StringBuffer &StringBuffer::append(const char32_t *p_str, int p_clip_to_len) { int len = 0; while ((p_clip_to_len < 0 || len < p_clip_to_len) && p_str[len]) { ++len; } reserve(string_length + len + 1); - memcpy(&(current_buffer_ptr()[string_length]), p_str, len * sizeof(CharType)); + memcpy(&(current_buffer_ptr()[string_length]), p_str, len * sizeof(char32_t)); string_length += len; return *this; @@ -125,7 +125,7 @@ StringBuffer &StringBuffer::reserve(int p_ bool need_copy = string_length > 0 && buffer.empty(); buffer.resize(next_power_of_2(p_size)); if (need_copy) { - memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(CharType)); + memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t)); } return *this; diff --git a/core/string_builder.cpp b/core/string_builder.cpp index c8d6498f27..dec299ffa3 100644 --- a/core/string_builder.cpp +++ b/core/string_builder.cpp @@ -61,7 +61,7 @@ String StringBuilder::as_string() const { return ""; } - CharType *buffer = memnew_arr(CharType, string_length); + char32_t *buffer = memnew_arr(char32_t, string_length); int current_position = 0; @@ -73,7 +73,7 @@ String StringBuilder::as_string() const { // Godot string const String &s = strings[godot_string_elem]; - memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(CharType)); + memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(char32_t)); current_position += s.length(); diff --git a/core/string_name.cpp b/core/string_name.cpp index cbf6009681..6260e3ce8c 100644 --- a/core/string_name.cpp +++ b/core/string_name.cpp @@ -317,7 +317,7 @@ StringName StringName::search(const char *p_name) { return StringName(); //does not exist } -StringName StringName::search(const CharType *p_name) { +StringName StringName::search(const char32_t *p_name) { ERR_FAIL_COND_V(!configured, StringName()); ERR_FAIL_COND_V(!p_name, StringName()); diff --git a/core/string_name.h b/core/string_name.h index 886ddd0ee7..4f90479bda 100644 --- a/core/string_name.h +++ b/core/string_name.h @@ -122,7 +122,7 @@ public: } static StringName search(const char *p_name); - static StringName search(const CharType *p_name); + static StringName search(const char32_t *p_name); static StringName search(const String &p_name); struct AlphCompare { diff --git a/core/type_info.h b/core/type_info.h index e3d2b5bd53..3c7f59bb84 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -132,7 +132,8 @@ MAKE_TYPE_INFO_WITH_META(uint32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_ MAKE_TYPE_INFO_WITH_META(int32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT32) MAKE_TYPE_INFO_WITH_META(uint64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT64) MAKE_TYPE_INFO_WITH_META(int64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT64) -MAKE_TYPE_INFO(wchar_t, Variant::INT) +MAKE_TYPE_INFO(char16_t, Variant::INT) +MAKE_TYPE_INFO(char32_t, Variant::INT) MAKE_TYPE_INFO_WITH_META(float, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_FLOAT) MAKE_TYPE_INFO_WITH_META(double, Variant::FLOAT, GodotTypeInfo::METADATA_REAL_IS_DOUBLE) diff --git a/core/ustring.cpp b/core/ustring.cpp index 9d2d938eaf..d5afbc2b47 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -39,7 +39,6 @@ #include "core/ucaps.h" #include "core/variant.h" -#include #include #ifndef NO_USE_STDLIB @@ -62,9 +61,10 @@ #define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F')) const char CharString::_null = 0; -const CharType String::_null = 0; +const char16_t Char16String::_null = 0; +const char32_t String::_null = 0; -bool is_symbol(CharType c) { +bool is_symbol(char32_t c) { return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); } @@ -96,7 +96,65 @@ bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { } } -/** STRING **/ +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ + +bool Char16String::operator<(const Char16String &p_right) const { + if (length() == 0) { + return p_right.length() != 0; + } + + return is_str_less(get_data(), p_right.get_data()); +} + +Char16String &Char16String::operator+=(char16_t p_char) { + resize(size() ? size() + 1 : 2); + set(length(), 0); + set(length() - 1, p_char); + + return *this; +} + +Char16String &Char16String::operator=(const char16_t *p_cstr) { + copy_from(p_cstr); + return *this; +} + +const char16_t *Char16String::get_data() const { + if (size()) { + return &operator[](0); + } else { + return u""; + } +} + +void Char16String::copy_from(const char16_t *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + const char16_t *s = p_cstr; + for (; *s; s++) { + } + size_t len = s - p_cstr; + + if (len == 0) { + resize(0); + return; + } + + Error err = resize(++len); // include terminating null char + + ERR_FAIL_COND_MSG(err != OK, "Failed to copy char16_t string."); + + memcpy(ptrw(), p_cstr, len * sizeof(char16_t)); +} + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ bool CharString::operator<(const CharString &p_right) const { if (length() == 0) { @@ -114,6 +172,11 @@ CharString &CharString::operator+=(char p_char) { return *this; } +CharString &CharString::operator=(const char *p_cstr) { + copy_from(p_cstr); + return *this; +} + const char *CharString::get_data() const { if (size()) { return &operator[](0); @@ -122,11 +185,6 @@ const char *CharString::get_data() const { } } -CharString &CharString::operator=(const char *p_cstr) { - copy_from(p_cstr); - return *this; -} - void CharString::copy_from(const char *p_cstr) { if (!p_cstr) { resize(0); @@ -147,7 +205,44 @@ void CharString::copy_from(const char *p_cstr) { memcpy(ptrw(), p_cstr, len); } +/*************************************************************************/ +/* String */ +/*************************************************************************/ + +//TODO: move to TextServer +//kind of poor should be rewritten properly +String String::word_wrap(int p_chars_per_line) const { + int from = 0; + int last_space = 0; + String ret; + for (int i = 0; i < length(); i++) { + if (i - from >= p_chars_per_line) { + if (last_space == -1) { + ret += substr(from, i - from + 1) + "\n"; + } else { + ret += substr(from, last_space - from) + "\n"; + i = last_space; //rewind + } + from = i + 1; + last_space = -1; + } else if (operator[](i) == ' ' || operator[](i) == '\t') { + last_space = i; + } else if (operator[](i) == '\n') { + ret += substr(from, i - from) + "\n"; + from = i + 1; + last_space = -1; + } + } + + if (from < length()) { + ret += substr(from, length()); + } + + return ret; +} + void String::copy_from(const char *p_cstr) { + // copy Latin-1 encoded c-string directly if (!p_cstr) { resize(0); return; @@ -166,21 +261,100 @@ void String::copy_from(const char *p_cstr) { resize(len + 1); // include 0 - CharType *dst = this->ptrw(); + char32_t *dst = this->ptrw(); for (int i = 0; i < len + 1; i++) { dst[i] = p_cstr[i]; } } -void String::copy_from(const CharType *p_cstr, const int p_clip_to) { +void String::copy_from(const char *p_cstr, const int p_clip_to) { + // copy Latin-1 encoded c-string directly if (!p_cstr) { resize(0); return; } int len = 0; - const CharType *ptr = p_cstr; + const char *ptr = p_cstr; + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { + len++; + } + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include 0 + + char32_t *dst = this->ptrw(); + + for (int i = 0; i < len; i++) { + dst[i] = p_cstr[i]; + } + dst[len] = 0; +} + +void String::copy_from(const wchar_t *p_cstr) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr); +#else + // wchar_t is 32-bit, copy directly + copy_from((const char32_t *)p_cstr); +#endif +} + +void String::copy_from(const wchar_t *p_cstr, const int p_clip_to) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr, p_clip_to); +#else + // wchar_t is 32-bit, copy directly + copy_from((const char32_t *)p_cstr, p_clip_to); +#endif +} + +void String::copy_from(const char32_t &p_char) { + resize(2); + if ((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char, 16) + "."); + set(0, 0xfffd); + } else { + set(0, p_char); + } + set(1, 0); +} + +void String::copy_from(const char32_t *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const char32_t *ptr = p_cstr; + while (*(ptr++) != 0) { + len++; + } + + if (len == 0) { + resize(0); + return; + } + + copy_from_unchecked(p_cstr, len); +} + +void String::copy_from(const char32_t *p_cstr, const int p_clip_to) { + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const char32_t *ptr = p_cstr; while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { len++; } @@ -197,48 +371,32 @@ void String::copy_from(const CharType *p_cstr, const int p_clip_to) { // p_char != nullptr // p_length > 0 // p_length <= p_char strlen -void String::copy_from_unchecked(const CharType *p_char, const int p_length) { +void String::copy_from_unchecked(const char32_t *p_char, const int p_length) { resize(p_length + 1); set(p_length, 0); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); for (int i = 0; i < p_length; i++) { - dst[i] = p_char[i]; - } -} - -void String::copy_from(const CharType &p_char) { - resize(2); - set(0, p_char); - set(1, 0); -} - -bool String::operator==(const String &p_str) const { - if (length() != p_str.length()) { - return false; - } - if (empty()) { - return true; - } - - int l = length(); - - const CharType *src = c_str(); - const CharType *dst = p_str.c_str(); - - /* Compare char by char */ - for (int i = 0; i < l; i++) { - if (src[i] != dst[i]) { - return false; + if ((p_char[i] >= 0xd800 && p_char[i] <= 0xdfff) || (p_char[i] > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char[i], 16) + "."); + dst[i] = 0xfffd; + } else { + dst[i] = p_char[i]; } } - - return true; } -bool String::operator!=(const String &p_str) const { - return !(*this == p_str); +void String::operator=(const char *p_str) { + copy_from(p_str); +} + +void String::operator=(const char32_t *p_str) { + copy_from(p_str); +} + +void String::operator=(const wchar_t *p_str) { + copy_from(p_str); } String String::operator+(const String &p_str) const { @@ -247,6 +405,28 @@ String String::operator+(const String &p_str) const { return res; } +String operator+(const char *p_chr, const String &p_str) { + String tmp = p_chr; + tmp += p_str; + return tmp; +} + +String operator+(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + String tmp = String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bi + String tmp = (const char32_t *)p_chr; +#endif + tmp += p_str; + return tmp; +} + +String operator+(char32_t p_chr, const String &p_str) { + return (String::chr(p_chr) + p_str); +} + String &String::operator+=(const String &p_str) { if (empty()) { *this = p_str; @@ -261,8 +441,8 @@ String &String::operator+=(const String &p_str) { resize(length() + p_str.size()); - const CharType *src = p_str.c_str(); - CharType *dst = ptrw(); + const char32_t *src = p_str.get_data(); + char32_t *dst = ptrw(); set(length(), 0); @@ -273,19 +453,6 @@ String &String::operator+=(const String &p_str) { return *this; } -String &String::operator+=(const CharType *p_str) { - *this += String(p_str); - return *this; -} - -String &String::operator+=(CharType p_char) { - resize(size() ? size() + 1 : 2); - set(length(), 0); - set(length() - 1, p_char); - - return *this; -} - String &String::operator+=(const char *p_str) { if (!p_str || p_str[0] == 0) { return *this; @@ -301,7 +468,7 @@ String &String::operator+=(const char *p_str) { resize(from + src_len + 1); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); set(length(), 0); @@ -312,38 +479,37 @@ String &String::operator+=(const char *p_str) { return *this; } -void String::operator=(const char *p_str) { - copy_from(p_str); +String &String::operator+=(const wchar_t *p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + *this += String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit + *this += String((const char32_t *)p_str); +#endif + return *this; } -void String::operator=(const CharType *p_str) { - copy_from(p_str); +String &String::operator+=(const char32_t *p_str) { + *this += String(p_str); + return *this; } -bool String::operator==(const StrRange &p_str_range) const { - int len = p_str_range.len; - - if (length() != len) { - return false; - } - if (empty()) { - return true; +String &String::operator+=(char32_t p_char) { + resize(size() ? size() + 1 : 2); + set(length(), 0); + if ((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff)) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(p_char, 16) + "."); + set(length() - 1, 0xfffd); + } else { + set(length() - 1, p_char); } - const CharType *c_str = p_str_range.c_str; - const CharType *dst = &operator[](0); - - /* Compare char by char */ - for (int i = 0; i < len; i++) { - if (c_str[i] != dst[i]) { - return false; - } - } - - return true; + return *this; } bool String::operator==(const char *p_str) const { + // compare Latin-1 encoded c-string int len = 0; const char *aux = p_str; @@ -360,11 +526,11 @@ bool String::operator==(const char *p_str) const { int l = length(); - const CharType *dst = c_str(); + const char32_t *dst = get_data(); - /* Compare char by char */ + // Compare char by char for (int i = 0; i < l; i++) { - if (p_str[i] != dst[i]) { + if ((char32_t)p_str[i] != dst[i]) { return false; } } @@ -372,9 +538,19 @@ bool String::operator==(const char *p_str) const { return true; } -bool String::operator==(const CharType *p_str) const { +bool String::operator==(const wchar_t *p_str) const { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + return *this == String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit, compare char by char + return *this == (const char32_t *)p_str; +#endif +} + +bool String::operator==(const char32_t *p_str) const { int len = 0; - const CharType *aux = p_str; + const char32_t *aux = p_str; while (*(aux++) != 0) { len++; @@ -389,7 +565,7 @@ bool String::operator==(const CharType *p_str) const { int l = length(); - const CharType *dst = c_str(); + const char32_t *dst = get_data(); /* Compare char by char */ for (int i = 0; i < l; i++) { @@ -401,23 +577,80 @@ bool String::operator==(const CharType *p_str) const { return true; } -bool String::operator!=(const char *p_str) const { - return (!(*this == p_str)); -} - -bool String::operator!=(const CharType *p_str) const { - return (!(*this == p_str)); -} - -bool String::operator<(const CharType *p_str) const { - if (empty() && p_str[0] == 0) { +bool String::operator==(const String &p_str) const { + if (length() != p_str.length()) { return false; } if (empty()) { return true; } - return is_str_less(c_str(), p_str); + int l = length(); + + const char32_t *src = get_data(); + const char32_t *dst = p_str.get_data(); + + /* Compare char by char */ + for (int i = 0; i < l; i++) { + if (src[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool String::operator==(const StrRange &p_str_range) const { + int len = p_str_range.len; + + if (length() != len) { + return false; + } + if (empty()) { + return true; + } + + const char32_t *c_str = p_str_range.c_str; + const char32_t *dst = &operator[](0); + + /* Compare char by char */ + for (int i = 0; i < len; i++) { + if (c_str[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool operator==(const char *p_chr, const String &p_str) { + return p_str == p_chr; +} + +bool operator==(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return p_str == String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bi + return p_str == String((const char32_t *)p_chr); +#endif +} + +bool String::operator!=(const char *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator!=(const wchar_t *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator!=(const char32_t *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator!=(const String &p_str) const { + return !((*this == p_str)); } bool String::operator<=(const String &p_str) const { @@ -431,12 +664,39 @@ bool String::operator<(const char *p_str) const { if (empty()) { return true; } + return is_str_less(get_data(), p_str); +} - return is_str_less(c_str(), p_str); +bool String::operator<(const wchar_t *p_str) const { + if (empty() && p_str[0] == 0) { + return false; + } + if (empty()) { + return true; + } + +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return is_str_less(get_data(), String::utf16((const char16_t *)p_str).get_data()); +#else + // wchar_t is 32-bit + return is_str_less(get_data(), (const char32_t *)p_str); +#endif +} + +bool String::operator<(const char32_t *p_str) const { + if (empty() && p_str[0] == 0) { + return false; + } + if (empty()) { + return true; + } + + return is_str_less(get_data(), p_str); } bool String::operator<(const String &p_str) const { - return operator<(p_str.c_str()); + return operator<(p_str.get_data()); } signed char String::nocasecmp_to(const String &p_str) const { @@ -450,8 +710,8 @@ signed char String::nocasecmp_to(const String &p_str) const { return 1; } - const CharType *that_str = p_str.c_str(); - const CharType *this_str = c_str(); + const char32_t *that_str = p_str.get_data(); + const char32_t *this_str = get_data(); while (true) { if (*that_str == 0 && *this_str == 0) { @@ -482,8 +742,8 @@ signed char String::casecmp_to(const String &p_str) const { return 1; } - const CharType *that_str = p_str.c_str(); - const CharType *this_str = c_str(); + const char32_t *that_str = p_str.get_data(); + const char32_t *this_str = get_data(); while (true) { if (*that_str == 0 && *this_str == 0) { @@ -504,8 +764,8 @@ signed char String::casecmp_to(const String &p_str) const { } signed char String::naturalnocasecmp_to(const String &p_str) const { - const CharType *this_str = c_str(); - const CharType *that_str = p_str.c_str(); + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); if (this_str && that_str) { while (*this_str == '.' || *that_str == '.') { @@ -571,6 +831,11 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { return 0; } +const char32_t *String::get_data() const { + static const char32_t zero = 0; + return size() ? &operator[](0) : &zero; +} + void String::erase(int p_pos, int p_chars) { *this = left(p_pos) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); } @@ -593,7 +858,7 @@ String String::capitalize() const { } String String::camelcase_to_underscore(bool lowercase) const { - const CharType *cstr = c_str(); + const char32_t *cstr = get_data(); String new_string; const char A = 'A', Z = 'Z'; const char a = 'a', z = 'z'; @@ -705,7 +970,7 @@ String String::get_slice(String p_splitter, int p_slice) const { return ""; //no find! } -String String::get_slicec(CharType p_splitter, int p_slice) const { +String String::get_slicec(char32_t p_splitter, int p_slice) const { if (empty()) { return String(); } @@ -714,7 +979,7 @@ String String::get_slicec(CharType p_splitter, int p_slice) const { return String(); } - const CharType *c = this->ptr(); + const char32_t *c = this->ptr(); int i = 0; int prev = 0; int count = 0; @@ -851,7 +1116,7 @@ Vector String::split_floats(const String &p_splitter, bool p_allow_empty) end = len; } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_float(&c_str()[from])); + ret.push_back(String::to_float(&get_data()[from])); } if (end == len) { @@ -880,7 +1145,7 @@ Vector String::split_floats_mk(const Vector &p_splitters, bool p_ } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_float(&c_str()[from])); + ret.push_back(String::to_float(&get_data()[from])); } if (end == len) { @@ -904,7 +1169,7 @@ Vector String::split_ints(const String &p_splitter, bool p_allow_empty) con end = len; } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_int(&c_str()[from], end - from)); + ret.push_back(String::to_int(&get_data()[from], end - from)); } if (end == len) { @@ -933,7 +1198,7 @@ Vector String::split_ints_mk(const Vector &p_splitters, bool p_allo } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_int(&c_str()[from], end - from)); + ret.push_back(String::to_int(&get_data()[from], end - from)); } if (end == len) { @@ -946,7 +1211,7 @@ Vector String::split_ints_mk(const Vector &p_splitters, bool p_allo return ret; } -String String::join(Vector parts) { +String String::join(Vector parts) const { String ret; for (int i = 0; i < parts.size(); ++i) { if (i > 0) { @@ -957,11 +1222,11 @@ String String::join(Vector parts) { return ret; } -CharType String::char_uppercase(CharType p_char) { +char32_t String::char_uppercase(char32_t p_char) { return _find_upper(p_char); } -CharType String::char_lowercase(CharType p_char) { +char32_t String::char_lowercase(char32_t p_char) { return _find_lower(p_char); } @@ -969,8 +1234,8 @@ String String::to_upper() const { String upper = *this; for (int i = 0; i < upper.size(); i++) { - const CharType s = upper[i]; - const CharType t = _find_upper(s); + const char32_t s = upper[i]; + const char32_t t = _find_upper(s); if (s != t) { // avoid copy on write upper[i] = t; } @@ -983,8 +1248,8 @@ String String::to_lower() const { String lower = *this; for (int i = 0; i < lower.size(); i++) { - const CharType s = lower[i]; - const CharType t = _find_lower(s); + const char32_t s = lower[i]; + const char32_t t = _find_lower(s); if (s != t) { // avoid copy on write lower[i] = t; } @@ -993,34 +1258,8 @@ String String::to_lower() const { return lower; } -const CharType *String::c_str() const { - static const CharType zero = 0; - - return size() ? &operator[](0) : &zero; -} - -String String::md5(const uint8_t *p_md5) { - return String::hex_encode_buffer(p_md5, 16); -} - -String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { - static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - - String ret; - char v[2] = { 0, 0 }; - - for (int i = 0; i < p_len; i++) { - v[0] = hex[p_buffer[i] >> 4]; - ret += v; - v[0] = hex[p_buffer[i] & 0xF]; - ret += v; - } - - return ret; -} - -String String::chr(CharType p_char) { - CharType c[2] = { p_char, 0 }; +String String::chr(char32_t p_char) { + char32_t c[2] = { p_char, 0 }; return String(c); } @@ -1028,6 +1267,14 @@ String String::num(double p_num, int p_decimals) { if (Math::is_nan(p_num)) { return "nan"; } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } #ifndef NO_USE_STDLIB if (p_decimals > 16) { @@ -1106,7 +1353,7 @@ String String::num(double p_num, int p_decimals) { /* decimal part */ if (p_decimals > 0 || (p_decimals == -1 && (int)p_num != p_num)) { - double dec = p_num - (float)((int)p_num); + double dec = p_num - (double)((int)p_num); int digit = 0; if (p_decimals > MAX_DIGITS) @@ -1125,7 +1372,7 @@ String String::num(double p_num, int p_decimals) { if (digit == MAX_DIGITS) //no point in going to infinite break; - if ((dec - (float)((int)dec)) < 1e-6) + if ((dec - (double)((int)dec)) < 1e-6) break; } @@ -1159,7 +1406,7 @@ String String::num(double p_num, int p_decimals) { s = "0"; else { while (intn) { - CharType num = '0' + (intn % 10); + char32_t num = '0' + (intn % 10); intn /= 10; s = num + s; } @@ -1188,7 +1435,7 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { } String s; s.resize(chars + 1); - CharType *c = s.ptrw(); + char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; do { @@ -1221,7 +1468,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { String s; s.resize(chars + 1); - CharType *c = s.ptrw(); + char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; do { @@ -1240,6 +1487,18 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { } String String::num_real(double p_num) { + if (Math::is_nan(p_num)) { + return "nan"; + } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } + String s; String sd; /* integer part */ @@ -1251,7 +1510,7 @@ String String::num_real(double p_num) { /* decimal part */ if ((int)p_num != p_num) { - double dec = p_num - (float)((int)p_num); + double dec = p_num - (double)((int)p_num); int digit = 0; int decimals = MAX_DIGITS; @@ -1265,7 +1524,7 @@ String String::num_real(double p_num) { dec_max = dec_max * 10 + 9; digit++; - if ((dec - (float)((int)dec)) < 1e-6) { + if ((dec - (double)((int)dec)) < 1e-6) { break; } @@ -1302,7 +1561,7 @@ String String::num_real(double p_num) { s = "0"; } else { while (intn) { - CharType num = '0' + (intn % 10); + char32_t num = '0' + (intn % 10); intn /= 10; s = num + s; } @@ -1319,6 +1578,14 @@ String String::num_scientific(double p_num) { if (Math::is_nan(p_num)) { return "nan"; } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } #ifndef NO_USE_STDLIB char buf[256]; @@ -1348,6 +1615,26 @@ String String::num_scientific(double p_num) { #endif } +String String::md5(const uint8_t *p_md5) { + return String::hex_encode_buffer(p_md5, 16); +} + +String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + String ret; + char v[2] = { 0, 0 }; + + for (int i = 0; i < p_len; i++) { + v[0] = hex[p_buffer[i] >> 4]; + ret += v; + v[0] = hex[p_buffer[i] & 0xF]; + ret += v; + } + + return ret; +} + CharString String::ascii(bool p_allow_extended) const { if (!length()) { return CharString(); @@ -1357,7 +1644,13 @@ CharString String::ascii(bool p_allow_extended) const { cs.resize(size()); for (int i = 0; i < size(); i++) { - cs[i] = operator[](i); + char32_t c = operator[](i); + if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) { + cs[i] = c; + } else { + print_error("Unicode parsing error: Cannot represent " + num_int64(c, 16) + " as ASCII/Latin-1 character."); + cs[i] = 0x20; + } } return cs; @@ -1371,7 +1664,7 @@ String String::utf8(const char *p_utf8, int p_len) { } bool String::parse_utf8(const char *p_utf8, int p_len) { -#define _UNICERROR(m_err) print_line("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); +#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); if (!p_utf8) { return true; @@ -1384,9 +1677,9 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { /* HANDLE BOM (Byte Order Mark) */ if (p_len < 0 || p_len >= 3) { - bool has_bom = uint8_t(p_utf8[0]) == 0xEF && uint8_t(p_utf8[1]) == 0xBB && uint8_t(p_utf8[2]) == 0xBF; + bool has_bom = uint8_t(p_utf8[0]) == 0xef && uint8_t(p_utf8[1]) == 0xbb && uint8_t(p_utf8[2]) == 0xbf; if (has_bom) { - //just skip it + //8-bit encoding, byte order has no meaning in UTF-8, just skip it if (p_len >= 0) { p_len -= 3; } @@ -1405,24 +1698,19 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { /* Determine the number of characters in sequence */ if ((c & 0x80) == 0) { skip = 0; - } else if ((c & 0xE0) == 0xC0) { + } else if ((c & 0xe0) == 0xc0) { skip = 1; - } else if ((c & 0xF0) == 0xE0) { + } else if ((c & 0xf0) == 0xe0) { skip = 2; - } else if ((c & 0xF8) == 0xF0) { + } else if ((c & 0xf8) == 0xf0) { skip = 3; - } else if ((c & 0xFC) == 0xF8) { - skip = 4; - } else if ((c & 0xFE) == 0xFC) { - skip = 5; } else { - _UNICERROR("invalid skip"); + _UNICERROR("invalid skip at " + num_int64(cstr_size)); return true; //invalid utf8 } - if (skip == 1 && (c & 0x1E) == 0) { - //printf("overlong rejected\n"); - _UNICERROR("overlong rejected"); + if (skip == 1 && (c & 0x1e) == 0) { + _UNICERROR("overlong rejected at " + num_int64(cstr_size)); return true; //reject overlong } @@ -1448,7 +1736,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } resize(str_size + 1); - CharType *dst = ptrw(); + char32_t *dst = ptrw(); dst[str_size] = 0; while (cstr_size) { @@ -1457,19 +1745,14 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { /* Determine the number of characters in sequence */ if ((*p_utf8 & 0x80) == 0) { len = 1; - } else if ((*p_utf8 & 0xE0) == 0xC0) { + } else if ((*p_utf8 & 0xe0) == 0xc0) { len = 2; - } else if ((*p_utf8 & 0xF0) == 0xE0) { + } else if ((*p_utf8 & 0xf0) == 0xe0) { len = 3; - } else if ((*p_utf8 & 0xF8) == 0xF0) { + } else if ((*p_utf8 & 0xf8) == 0xf0) { len = 4; - } else if ((*p_utf8 & 0xFC) == 0xF8) { - len = 5; - } else if ((*p_utf8 & 0xFE) == 0xFC) { - len = 6; } else { _UNICERROR("invalid len"); - return true; //invalid UTF8 } @@ -1479,7 +1762,6 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } if (len == 2 && (*p_utf8 & 0x1E) == 0) { - //printf("overlong rejected\n"); _UNICERROR("no space left"); return true; //reject overlong } @@ -1491,24 +1773,23 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { if (len == 1) { unichar = *p_utf8; } else { - unichar = (0xFF >> (len + 1)) & *p_utf8; + unichar = (0xff >> (len + 1)) & *p_utf8; for (int i = 1; i < len; i++) { - if ((p_utf8[i] & 0xC0) != 0x80) { + if ((p_utf8[i] & 0xc0) != 0x80) { _UNICERROR("invalid utf8"); return true; //invalid utf8 } - if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7F) >> (7 - len)) == 0) { + if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7f) >> (7 - len)) == 0) { _UNICERROR("invalid utf8 overlong"); return true; //no overlong } - unichar = (unichar << 6) | (p_utf8[i] & 0x3F); + unichar = (unichar << 6) | (p_utf8[i] & 0x3f); } } - - //printf("char %i, len %i\n",unichar,len); - if (sizeof(wchar_t) == 2 && unichar > 0xFFFF) { - unichar = ' '; //too long for windows + if (unichar >= 0xd800 && unichar <= 0xdfff) { + _UNICERROR("invalid code point"); + return CharString(); } *(dst++) = unichar; @@ -1517,6 +1798,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } return false; +#undef _UNICERROR } CharString String::utf8() const { @@ -1525,7 +1807,7 @@ CharString String::utf8() const { return CharString(); } - const CharType *d = &operator[](0); + const char32_t *d = &operator[](0); int fl = 0; for (int i = 0; i < l; i++) { uint32_t c = d[i]; @@ -1535,13 +1817,15 @@ CharString String::utf8() const { fl += 2; } else if (c <= 0xffff) { // 16 bits fl += 3; - } else if (c <= 0x001fffff) { // 21 bits + } else if (c <= 0x0010ffff) { // 21 bits fl += 4; - - } else if (c <= 0x03ffffff) { // 26 bits - fl += 5; - } else if (c <= 0x7fffffff) { // 31 bits - fl += 6; + } else { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return CharString(); + } + if (c >= 0xd800 && c <= 0xdfff) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return CharString(); } } @@ -1561,35 +1845,17 @@ CharString String::utf8() const { if (c <= 0x7f) { // 7 bits. APPEND_CHAR(c); } else if (c <= 0x7ff) { // 11 bits - APPEND_CHAR(uint32_t(0xc0 | ((c >> 6) & 0x1f))); // Top 5 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. } else if (c <= 0xffff) { // 16 bits - APPEND_CHAR(uint32_t(0xe0 | ((c >> 12) & 0x0f))); // Top 4 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Middle 6 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x001fffff) { // 21 bits - + } else { // 21 bits APPEND_CHAR(uint32_t(0xf0 | ((c >> 18) & 0x07))); // Top 3 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper middle 6 bits. APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x03ffffff) { // 26 bits - - APPEND_CHAR(uint32_t(0xf8 | ((c >> 24) & 0x03))); // Top 2 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. - } else if (c <= 0x7fffffff) { // 31 bits - - APPEND_CHAR(uint32_t(0xfc | ((c >> 30) & 0x01))); // Top 1 bit. - APPEND_CHAR(uint32_t(0x80 | ((c >> 24) & 0x3f))); // Upper upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Lower upper middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower lower middle 6 bits. - APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. } } #undef APPEND_CHAR @@ -1598,21 +1864,191 @@ CharString String::utf8() const { return utf8s; } -/* -String::String(CharType p_char) { +String String::utf16(const char16_t *p_utf16, int p_len) { + String ret; + ret.parse_utf16(p_utf16, p_len); - shared=nullptr; - copy_from(p_char); + return ret; } +bool String::parse_utf16(const char16_t *p_utf16, int p_len) { +#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); -*/ + if (!p_utf16) { + return true; + } + + String aux; + + int cstr_size = 0; + int str_size = 0; + + /* HANDLE BOM (Byte Order Mark) */ + bool byteswap = false; // assume correct endianness if no BOM found + if (p_len < 0 || p_len >= 1) { + bool has_bom = false; + if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is + has_bom = true; + byteswap = false; + } else if (uint16_t(p_utf16[0]) == 0xfffe) { // backwards BOM, swap bytes + has_bom = true; + byteswap = true; + } + if (has_bom) { + if (p_len >= 0) { + p_len -= 1; + } + p_utf16 += 1; + } + } + + { + const char16_t *ptrtmp = p_utf16; + const char16_t *ptrtmp_limit = &p_utf16[p_len]; + int skip = 0; + while (ptrtmp != ptrtmp_limit && *ptrtmp) { + uint32_t c = (byteswap) ? BSWAP16(*ptrtmp) : *ptrtmp; + if (skip == 0) { + if ((c & 0xfffffc00) == 0xd800) { + skip = 1; // lead surrogate + } else if ((c & 0xfffffc00) == 0xdc00) { + _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + return true; // invalid UTF16 + } else { + skip = 0; + } + str_size++; + } else { + if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate + --skip; + } else { + _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + return true; // invalid UTF16 + } + } + + cstr_size++; + ptrtmp++; + } + + if (skip) { + _UNICERROR("no space left"); + return true; // not enough space + } + } + + if (str_size == 0) { + clear(); + return false; + } + + resize(str_size + 1); + char32_t *dst = ptrw(); + dst[str_size] = 0; + + while (cstr_size) { + int len = 0; + uint32_t c = (byteswap) ? BSWAP16(*p_utf16) : *p_utf16; + + if ((c & 0xfffffc00) == 0xd800) { + len = 2; + } else { + len = 1; + } + + if (len > cstr_size) { + _UNICERROR("no space left"); + return true; //not enough space + } + + uint32_t unichar = 0; + if (len == 1) { + unichar = c; + } else { + uint32_t c2 = (byteswap) ? BSWAP16(p_utf16[1]) : p_utf16[1]; + unichar = (c << 10UL) + c2 - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + } + + *(dst++) = unichar; + cstr_size -= len; + p_utf16 += len; + } + + return false; +#undef _UNICERROR +} + +Char16String String::utf16() const { + int l = length(); + if (!l) { + return Char16String(); + } + + const char32_t *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0xffff) { // 16 bits. + fl += 1; + } else if (c <= 0x10ffff) { // 32 bits. + fl += 2; + } else { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return Char16String(); + } + if (c >= 0xd800 && c <= 0xdfff) { + print_error("Unicode parsing error: Invalid unicode codepoint " + num_int64(c, 16) + "."); + return Char16String(); + } + } + + Char16String utf16s; + if (fl == 0) { + return utf16s; + } + + utf16s.resize(fl + 1); + uint16_t *cdst = (uint16_t *)utf16s.get_data(); + +#define APPEND_CHAR(m_c) *(cdst++) = m_c + + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + + if (c <= 0xffff) { // 16 bits. + APPEND_CHAR(c); + } else { // 32 bits. + APPEND_CHAR(uint32_t((c >> 10) + 0xd7c0)); // lead surrogate. + APPEND_CHAR(uint32_t((c & 0x3ff) | 0xdc00)); // trail surrogate. + } + } +#undef APPEND_CHAR + *cdst = 0; //trailing zero + + return utf16s; +} String::String(const char *p_str) { copy_from(p_str); } -String::String(const CharType *p_str, int p_clip_to_len) { +String::String(const wchar_t *p_str) { + copy_from(p_str); +} + +String::String(const char32_t *p_str) { + copy_from(p_str); +} + +String::String(const char *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const wchar_t *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const char32_t *p_str, int p_clip_to_len) { copy_from(p_str, p_clip_to_len); } @@ -1620,7 +2056,6 @@ String::String(const StrRange &p_range) { if (!p_range.c_str) { return; } - copy_from(p_range.c_str, p_range.len); } @@ -1629,7 +2064,7 @@ int64_t String::hex_to_int(bool p_with_prefix) const { return 0; } - const CharType *s = ptr(); + const char32_t *s = ptr(); int64_t sign = s[0] == '-' ? -1 : 1; @@ -1647,7 +2082,7 @@ int64_t String::hex_to_int(bool p_with_prefix) const { int64_t hex = 0; while (*s) { - CharType c = LOWERCASE(*s); + char32_t c = LOWERCASE(*s); int64_t n; if (c >= '0' && c <= '9') { n = c - '0'; @@ -1672,7 +2107,7 @@ int64_t String::bin_to_int(bool p_with_prefix) const { return 0; } - const CharType *s = ptr(); + const char32_t *s = ptr(); int64_t sign = s[0] == '-' ? -1 : 1; @@ -1690,7 +2125,7 @@ int64_t String::bin_to_int(bool p_with_prefix) const { int64_t binary = 0; while (*s) { - CharType c = LOWERCASE(*s); + char32_t c = LOWERCASE(*s); int64_t n; if (c == '0' || c == '1') { n = c - '0'; @@ -1719,7 +2154,7 @@ int64_t String::to_int() const { int64_t sign = 1; for (int i = 0; i < to; i++) { - CharType c = operator[](i); + char32_t c = operator[](i); if (c >= '0' && c <= '9') { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); @@ -1765,6 +2200,37 @@ int64_t String::to_int(const char *p_str, int p_len) { return integer * sign; } +int64_t String::to_int(const wchar_t *p_str, int p_len) { + int to = 0; + if (p_len >= 0) { + to = p_len; + } else { + while (p_str[to] != 0 && p_str[to] != '.') { + to++; + } + } + + int64_t integer = 0; + int64_t sign = 1; + + for (int i = 0; i < to; i++) { + wchar_t c = p_str[i]; + if (c >= '0' && c <= '9') { + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + integer *= 10; + integer += c - '0'; + + } else if (c == '-' && integer == 0) { + sign = -sign; + } else if (c != ' ') { + break; + } + } + + return integer * sign; +} + bool String::is_numeric() const { if (length() == 0) { return false; @@ -1776,14 +2242,13 @@ bool String::is_numeric() const { } bool dot = false; for (int i = s; i < length(); i++) { - CharType c = operator[](i); + char32_t c = operator[](i); if (c == '.') { if (dot) { return false; } dot = true; - } - if (c < '0' || c > '9') { + } else if (c < '0' || c > '9') { return false; } } @@ -1945,11 +2410,11 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point } expSign = false; } - if (!IS_DIGIT(CharType(*p))) { + if (!IS_DIGIT(char32_t(*p))) { p = pExp; goto done; } - while (IS_DIGIT(CharType(*p))) { + while (IS_DIGIT(char32_t(*p))) { exp = exp * 10 + (*p - '0'); p += 1; } @@ -2007,19 +2472,18 @@ done: #define READING_DONE 4 double String::to_float(const char *p_str) { -#ifndef NO_USE_STDLIB return built_in_strtod(p_str); -//return atof(p_str); DOES NOT WORK ON ANDROID(??) -#else - return built_in_strtod(p_str); -#endif } -double String::to_float(const CharType *p_str, const CharType **r_end) { - return built_in_strtod(p_str, (CharType **)r_end); +double String::to_float(const char32_t *p_str, const char32_t **r_end) { + return built_in_strtod(p_str, (char32_t **)r_end); } -int64_t String::to_int(const CharType *p_str, int p_len, bool p_clamp) { +double String::to_float(const wchar_t *p_str, const wchar_t **r_end) { + return built_in_strtod(p_str, (wchar_t **)r_end); +} + +int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { if (p_len == 0 || !p_str[0]) { return 0; } @@ -2029,11 +2493,11 @@ int64_t String::to_int(const CharType *p_str, int p_len, bool p_clamp) { int64_t sign = 1; int reading = READING_SIGN; - const CharType *str = p_str; - const CharType *limit = &p_str[p_len]; + const char32_t *str = p_str; + const char32_t *limit = &p_str[p_len]; while (*str && reading != READING_DONE && str != limit) { - CharType c = *(str++); + char32_t c = *(str++); switch (reading) { case READING_SIGN: { if (c >= '0' && c <= '9') { @@ -2087,26 +2551,7 @@ double String::to_float() const { if (empty()) { return 0; } -#ifndef NO_USE_STDLIB - return built_in_strtod(c_str()); -//return wcstod(c_str(),nullptr ); DOES NOT WORK ON ANDROID :( -#else - return built_in_strtod(c_str()); -#endif -} - -bool operator==(const char *p_chr, const String &p_str) { - return p_str == p_chr; -} - -String operator+(const char *p_chr, const String &p_str) { - String tmp = p_chr; - tmp += p_str; - return tmp; -} - -String operator+(CharType p_chr, const String &p_str) { - return (String::chr(p_chr) + p_str); + return built_in_strtod(get_data()); } uint32_t String::hash(const char *p_cstr) { @@ -2129,7 +2574,7 @@ uint32_t String::hash(const char *p_cstr, int p_len) { return hashv; } -uint32_t String::hash(const CharType *p_cstr, int p_len) { +uint32_t String::hash(const wchar_t *p_cstr, int p_len) { uint32_t hashv = 5381; for (int i = 0; i < p_len; i++) { hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ @@ -2138,7 +2583,27 @@ uint32_t String::hash(const CharType *p_cstr, int p_len) { return hashv; } -uint32_t String::hash(const CharType *p_cstr) { +uint32_t String::hash(const wchar_t *p_cstr) { + uint32_t hashv = 5381; + uint32_t c; + + while ((c = *p_cstr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const char32_t *p_cstr, int p_len) { + uint32_t hashv = 5381; + for (int i = 0; i < p_len; i++) { + hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const char32_t *p_cstr) { uint32_t hashv = 5381; uint32_t c; @@ -2152,7 +2617,7 @@ uint32_t String::hash(const CharType *p_cstr) { uint32_t String::hash() const { /* simple djb2 hashing */ - const CharType *chr = c_str(); + const char32_t *chr = get_data(); uint32_t hashv = 5381; uint32_t c; @@ -2166,7 +2631,7 @@ uint32_t String::hash() const { uint64_t String::hash64() const { /* simple djb2 hashing */ - const CharType *chr = c_str(); + const char32_t *chr = get_data(); uint64_t hashv = 5381; uint64_t c; @@ -2278,7 +2743,7 @@ String String::substr(int p_from, int p_chars) const { } String s = String(); - s.copy_from_unchecked(&c_str()[p_from], p_chars); + s.copy_from_unchecked(&get_data()[p_from], p_chars); return s; } @@ -2295,8 +2760,8 @@ int String::find(const String &p_str, int p_from) const { return -1; // won't find anything! } - const CharType *src = c_str(); - const CharType *str = p_str.c_str(); + const char32_t *src = get_data(); + const char32_t *str = p_str.get_data(); for (int i = p_from; i <= (len - src_len); i++) { bool found = true; @@ -2333,7 +2798,7 @@ int String::find(const char *p_str, int p_from) const { return -1; // won't find anything! } - const CharType *src = c_str(); + const char32_t *src = get_data(); int src_len = 0; while (p_str[src_len] != '\0') { @@ -2341,7 +2806,7 @@ int String::find(const char *p_str, int p_from) const { } if (src_len == 1) { - const char needle = p_str[0]; + const char32_t needle = p_str[0]; for (int i = p_from; i < len; i++) { if (src[i] == needle) { @@ -2360,7 +2825,7 @@ int String::find(const char *p_str, int p_from) const { return -1; } - if (src[read_pos] != p_str[j]) { + if (src[read_pos] != (char32_t)p_str[j]) { found = false; break; } @@ -2375,7 +2840,7 @@ int String::find(const char *p_str, int p_from) const { return -1; } -int String::find_char(const CharType &p_char, int p_from) const { +int String::find_char(const char32_t &p_char, int p_from) const { return _cowdata.find(p_char, p_from); } @@ -2396,7 +2861,7 @@ int String::findmk(const Vector &p_keys, int p_from, int *r_key) const { return -1; // won't find anything! } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i < len; i++) { bool found = true; @@ -2405,7 +2870,7 @@ int String::findmk(const Vector &p_keys, int p_from, int *r_key) const { if (r_key) { *r_key = k; } - const CharType *cmp = keys[k].c_str(); + const char32_t *cmp = keys[k].get_data(); int l = keys[k].length(); for (int j = 0; j < l; j++) { @@ -2445,7 +2910,7 @@ int String::findn(const String &p_str, int p_from) const { return -1; // won't find anything! } - const CharType *srcd = c_str(); + const char32_t *srcd = get_data(); for (int i = p_from; i <= (length() - src_len); i++) { bool found = true; @@ -2457,8 +2922,8 @@ int String::findn(const String &p_str, int p_from) const { return -1; } - CharType src = _find_lower(srcd[read_pos]); - CharType dst = _find_lower(p_str[j]); + char32_t src = _find_lower(srcd[read_pos]); + char32_t dst = _find_lower(p_str[j]); if (src != dst) { found = false; @@ -2495,7 +2960,7 @@ int String::rfind(const String &p_str, int p_from) const { return -1; // won't find anything! } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i >= 0; i--) { bool found = true; @@ -2542,7 +3007,7 @@ int String::rfindn(const String &p_str, int p_from) const { return -1; // won't find anything! } - const CharType *src = c_str(); + const char32_t *src = get_data(); for (int i = p_from; i >= 0; i--) { bool found = true; @@ -2554,8 +3019,8 @@ int String::rfindn(const String &p_str, int p_from) const { return -1; } - CharType srcc = _find_lower(src[read_pos]); - CharType dstc = _find_lower(p_str[j]); + char32_t srcc = _find_lower(src[read_pos]); + char32_t dstc = _find_lower(p_str[j]); if (srcc != dstc) { found = false; @@ -2589,8 +3054,8 @@ bool String::begins_with(const String &p_string) const { return true; } - const CharType *src = &p_string[0]; - const CharType *str = &operator[](0); + const char32_t *src = &p_string[0]; + const char32_t *str = &operator[](0); int i = 0; for (; i < l; i++) { @@ -2609,11 +3074,11 @@ bool String::begins_with(const char *p_string) const { return false; } - const CharType *str = &operator[](0); + const char32_t *str = &operator[](0); int i = 0; while (*p_string && i < l) { - if (*p_string != str[i]) { + if ((char32_t)*p_string != str[i]) { return false; } i++; @@ -2657,7 +3122,7 @@ int String::_count(const String &p_string, int p_from, int p_to, bool p_case_ins } if (p_from == 0 && p_to == len) { str = String(); - str.copy_from_unchecked(&c_str()[0], len); + str.copy_from_unchecked(&get_data()[0], len); } else { str = substr(p_from, p_to - p_from); } @@ -2695,14 +3160,14 @@ bool String::_base_is_subsequence_of(const String &p_string, bool case_insensiti return false; } - const CharType *src = &operator[](0); - const CharType *tgt = &p_string[0]; + const char32_t *src = &operator[](0); + const char32_t *tgt = &p_string[0]; for (; *src && *tgt; tgt++) { bool match = false; if (case_insensitive) { - CharType srcc = _find_lower(*src); - CharType tgtc = _find_lower(*tgt); + char32_t srcc = _find_lower(*src); + char32_t tgtc = _find_lower(*tgt); match = srcc == tgtc; } else { match = *src == *tgt; @@ -2748,8 +3213,8 @@ float String::similarity(const String &p_string) const { int src_size = src_bigrams.size(); int tgt_size = tgt_bigrams.size(); - float sum = src_size + tgt_size; - float inter = 0; + double sum = src_size + tgt_size; + double inter = 0; for (int i = 0; i < src_size; i++) { for (int j = 0; j < tgt_size; j++) { if (src_bigrams[i] == tgt_bigrams[j]) { @@ -2762,7 +3227,7 @@ float String::similarity(const String &p_string) const { return (2.0f * inter) / sum; } -static bool _wildcard_match(const CharType *p_pattern, const CharType *p_string, bool p_case_sensitive) { +static bool _wildcard_match(const char32_t *p_pattern, const char32_t *p_string, bool p_case_sensitive) { switch (*p_pattern) { case '\0': return !*p_string; @@ -2781,14 +3246,14 @@ bool String::match(const String &p_wildcard) const { return false; } - return _wildcard_match(p_wildcard.c_str(), c_str(), true); + return _wildcard_match(p_wildcard.get_data(), get_data(), true); } bool String::matchn(const String &p_wildcard) const { if (!p_wildcard.length() || !length()) { return false; } - return _wildcard_match(p_wildcard.c_str(), c_str(), false); + return _wildcard_match(p_wildcard.get_data(), get_data(), false); } String String::format(const Variant &values, String placeholder) const { @@ -2938,9 +3403,10 @@ String String::repeat(int p_count) const { ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); String new_string; - const CharType *src = this->c_str(); + const char32_t *src = this->get_data(); new_string.resize(length() * p_count + 1); + new_string[length() * p_count] = 0; for (int i = 0; i < p_count; i++) { for (int j = 0; j < length(); j++) { @@ -2975,7 +3441,7 @@ String String::right(int p_pos) const { return substr(p_pos, (length() - p_pos)); } -CharType String::ord_at(int p_idx) const { +char32_t String::ord_at(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, length(), 0); return operator[](p_idx); } @@ -2989,7 +3455,7 @@ String String::dedent() const { int indent_stop = -1; for (int i = 0; i < length(); i++) { - CharType c = operator[](i); + char32_t c = operator[](i); if (c == '\n') { if (has_text) { new_string += substr(indent_stop, i - indent_stop); @@ -3218,7 +3684,7 @@ bool String::is_valid_identifier() const { return false; } - const wchar_t *str = &operator[](0); + const char32_t *str = &operator[](0); for (int i = 0; i < len; i++) { if (i == 0) { @@ -3237,36 +3703,14 @@ bool String::is_valid_identifier() const { return true; } -//kind of poor should be rewritten properly - -String String::word_wrap(int p_chars_per_line) const { - int from = 0; - int last_space = 0; - String ret; - for (int i = 0; i < length(); i++) { - if (i - from >= p_chars_per_line) { - if (last_space == -1) { - ret += substr(from, i - from + 1) + "\n"; - } else { - ret += substr(from, last_space - from) + "\n"; - i = last_space; //rewind - } - from = i + 1; - last_space = -1; - } else if (operator[](i) == ' ' || operator[](i) == '\t') { - last_space = i; - } else if (operator[](i) == '\n') { - ret += substr(from, i - from) + "\n"; - from = i + 1; - last_space = -1; - } +bool String::is_valid_string() const { + int l = length(); + const char32_t *src = get_data(); + bool valid = true; + for (int i = 0; i < l; i++) { + valid = valid && (src[i] < 0xd800 || (src[i] > 0xdfff && src[i] <= 0x10ffff)); } - - if (from < length()) { - ret += substr(from, length()); - } - - return ret; + return valid; } String String::http_escape() const { @@ -3297,9 +3741,9 @@ String String::http_unescape() const { String res; for (int i = 0; i < length(); ++i) { if (ord_at(i) == '%' && i + 2 < length()) { - CharType ord1 = ord_at(i + 1); + char32_t ord1 = ord_at(i + 1); if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { - CharType ord2 = ord_at(i + 2); + char32_t ord2 = ord_at(i + 2); if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, nullptr, 16); @@ -3389,18 +3833,18 @@ for (int i=1;i<32;i++) { return str; } -static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, CharType *p_dst) { +static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, char32_t *p_dst) { int len = 0; while (p_src_len) { if (*p_src == '&') { int eat = 0; if (p_src_len >= 4 && p_src[1] == '#') { - CharType c = 0; + char32_t c = 0; for (int i = 2; i < p_src_len; i++) { eat = i + 1; - CharType ct = p_src[i]; + char32_t ct = p_src[i]; if (ct == ';') { break; } else if (ct >= '0' && ct <= '9') { @@ -3476,12 +3920,12 @@ static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, Ch String String::xml_unescape() const { String str; int l = length(); - int len = _xml_unescape(c_str(), l, nullptr); + int len = _xml_unescape(get_data(), l, nullptr); if (len == 0) { return String(); } str.resize(len + 1); - _xml_unescape(c_str(), l, str.ptrw()); + _xml_unescape(get_data(), l, str.ptrw()); str[len] = 0; return str; } @@ -3602,7 +4046,7 @@ bool String::is_valid_hex_number(bool p_with_prefix) const { } for (int i = from; i < len; i++) { - CharType c = operator[](i); + char32_t c = operator[](i); if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { continue; } @@ -3917,7 +4361,7 @@ String String::percent_decode() const { String String::property_name_encode() const { // Escape and quote strings with extended ASCII or further Unicode characters // as well as '"', '=' or ' ' (32) - const CharType *cstr = c_str(); + const char32_t *cstr = get_data(); for (int i = 0; cstr[i]; i++) { if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { return "\"" + c_escape_multiline() + "\""; @@ -3984,7 +4428,7 @@ String String::lpad(int min_length, const String &character) const { // In case of an error, the string returned is the error description and "error" is true. String String::sprintf(const Array &values, bool *error) const { String formatted; - CharType *self = (CharType *)c_str(); + char32_t *self = (char32_t *)get_data(); bool in_format = false; int value_index = 0; int min_chars = 0; @@ -3997,7 +4441,7 @@ String String::sprintf(const Array &values, bool *error) const { *error = true; for (; *self; self++) { - const CharType c = *self; + const char32_t c = *self; if (in_format) { // We have % - lets see what else we get. switch (c) { @@ -4134,9 +4578,11 @@ String String::sprintf(const Array &values, bool *error) const { if (values[value_index].is_num()) { int value = values[value_index]; if (value < 0) { - return "unsigned byte integer is lower than maximum"; - } else if (value > 255) { - return "unsigned byte integer is greater than maximum"; + return "unsigned integer is lower than minimum"; + } else if (value >= 0xd800 && value <= 0xdfff) { + return "unsigned integer is invalid Unicode character"; + } else if (value > 0x10ffff) { + return "unsigned integer is greater than maximum"; } str = chr(values[value_index]); } else if (values[value_index].get_type() == Variant::STRING) { diff --git a/core/ustring.h b/core/ustring.h index 7a1c1a5232..65eeae6643 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -36,8 +36,13 @@ #include "core/typedefs.h" #include "core/vector.h" +/*************************************************************************/ +/* CharProxy */ +/*************************************************************************/ + template class CharProxy { + friend class Char16String; friend class CharString; friend class String; @@ -71,6 +76,54 @@ public: } }; +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ + +class Char16String { + CowData _cowdata; + static const char16_t _null; + +public: + _FORCE_INLINE_ char16_t *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char16_t *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + + _FORCE_INLINE_ char16_t get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char16_t &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ const char16_t &operator[](int p_index) const { + if (unlikely(p_index == _cowdata.size())) { + return _null; + } + + return _cowdata.get(p_index); + } + _FORCE_INLINE_ CharProxy operator[](int p_index) { return CharProxy(p_index, _cowdata); } + + _FORCE_INLINE_ Char16String() {} + _FORCE_INLINE_ Char16String(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ Char16String operator=(const Char16String &p_str) { + _cowdata._ref(p_str._cowdata); + return *this; + } + _FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); } + + Char16String &operator=(const char16_t *p_cstr); + bool operator<(const Char16String &p_right) const; + Char16String &operator+=(char16_t p_char); + int length() const { return size() ? size() - 1 : 0; } + const char16_t *get_data() const; + operator const char16_t *() const { return get_data(); }; + +protected: + void copy_from(const char16_t *p_cstr); +}; + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ + class CharString { CowData _cowdata; static const char _null; @@ -111,26 +164,35 @@ protected: void copy_from(const char *p_cstr); }; -typedef wchar_t CharType; +/*************************************************************************/ +/* String */ +/*************************************************************************/ struct StrRange { - const CharType *c_str; + const char32_t *c_str; int len; - StrRange(const CharType *p_c_str = nullptr, int p_len = 0) { + StrRange(const char32_t *p_c_str = nullptr, int p_len = 0) { c_str = p_c_str; len = p_len; } }; class String { - CowData _cowdata; - static const CharType _null; + CowData _cowdata; + static const char32_t _null; void copy_from(const char *p_cstr); - void copy_from(const CharType *p_cstr, const int p_clip_to = -1); - void copy_from(const CharType &p_char); - void copy_from_unchecked(const CharType *p_char, const int p_length); + void copy_from(const char *p_cstr, const int p_clip_to); + void copy_from(const wchar_t *p_cstr); + void copy_from(const wchar_t *p_cstr, const int p_clip_to); + void copy_from(const char32_t *p_cstr); + void copy_from(const char32_t *p_cstr, const int p_clip_to); + + void copy_from(const char32_t &p_char); + + void copy_from_unchecked(const char32_t *p_char, const int p_length); + bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const; @@ -140,48 +202,56 @@ public: npos = -1 /// operator[](int p_index) { return CharProxy(p_index, _cowdata); } + _FORCE_INLINE_ CharProxy operator[](int p_index) { return CharProxy(p_index, _cowdata); } bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; - //String operator+(CharType p_char) const; String &operator+=(const String &); - String &operator+=(CharType p_char); + String &operator+=(char32_t p_char); String &operator+=(const char *p_str); - String &operator+=(const CharType *p_str); + String &operator+=(const wchar_t *p_str); + String &operator+=(const char32_t *p_str); /* Compatibility Operators */ void operator=(const char *p_str); - void operator=(const CharType *p_str); + void operator=(const wchar_t *p_str); + void operator=(const char32_t *p_str); + bool operator==(const char *p_str) const; - bool operator==(const CharType *p_str) const; + bool operator==(const wchar_t *p_str) const; + bool operator==(const char32_t *p_str) const; bool operator==(const StrRange &p_str_range) const; + bool operator!=(const char *p_str) const; - bool operator!=(const CharType *p_str) const; - bool operator<(const CharType *p_str) const; + bool operator!=(const wchar_t *p_str) const; + bool operator!=(const char32_t *p_str) const; + + bool operator<(const char32_t *p_str) const; bool operator<(const char *p_str) const; + bool operator<(const wchar_t *p_str) const; + bool operator<(const String &p_str) const; bool operator<=(const String &p_str) const; @@ -189,7 +259,7 @@ public: signed char nocasecmp_to(const String &p_str) const; signed char naturalnocasecmp_to(const String &p_str) const; - const CharType *c_str() const; + const char32_t *get_data() const; /* standard size stuff */ _FORCE_INLINE_ int length() const { @@ -197,11 +267,13 @@ public: return s ? (s - 1) : 0; // length does not include zero } + bool is_valid_string() const; + /* complex helpers */ String substr(int p_from, int p_chars = -1) const; 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 + int find_char(const char32_t &p_char, int p_from = 0) const; ///< return <0 if failed int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive 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 @@ -238,26 +310,31 @@ public: static String num_real(double p_num); static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false); - static String chr(CharType p_char); + static String chr(char32_t p_char); static String md5(const uint8_t *p_md5); static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); bool is_numeric() const; - double to_float() const; + double to_float() const; int64_t hex_to_int(bool p_with_prefix = true) const; int64_t bin_to_int(bool p_with_prefix = true) const; int64_t to_int() const; + static int64_t to_int(const char *p_str, int p_len = -1); + static int64_t to_int(const wchar_t *p_str, int p_len = -1); + static int64_t to_int(const char32_t *p_str, int p_len = -1, bool p_clamp = false); + static double to_float(const char *p_str); - static double to_float(const CharType *p_str, const CharType **r_end = nullptr); - static int64_t to_int(const CharType *p_str, int p_len = -1, bool p_clamp = false); + static double to_float(const wchar_t *p_str, const wchar_t **r_end = nullptr); + static double to_float(const char32_t *p_str, const char32_t **r_end = nullptr); + String capitalize() const; String camelcase_to_underscore(bool lowercase = true) const; String get_with_code_lines() const; int get_slice_count(String p_splitter) const; String get_slice(String p_splitter, int p_slice) const; - String get_slicec(CharType p_splitter, int p_slice) const; + String get_slicec(char32_t p_splitter, int p_slice) const; Vector split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; Vector rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; @@ -267,10 +344,10 @@ public: Vector split_ints(const String &p_splitter, bool p_allow_empty = true) const; Vector split_ints_mk(const Vector &p_splitters, bool p_allow_empty = true) const; - String join(Vector parts); + String join(Vector parts) const; - static CharType char_uppercase(CharType p_char); - static CharType char_lowercase(CharType p_char); + static char32_t char_uppercase(char32_t p_char); + static char32_t char_lowercase(char32_t p_char); String to_upper() const; String to_lower() const; @@ -287,7 +364,7 @@ public: String get_extension() const; String get_basename() const; String plus_file(const String &p_file) const; - CharType ord_at(int p_idx) const; + char32_t ord_at(int p_idx) const; void erase(int p_pos, int p_chars); @@ -296,8 +373,14 @@ public: bool parse_utf8(const char *p_utf8, int p_len = -1); //return true on error static String utf8(const char *p_utf8, int p_len = -1); - static uint32_t hash(const CharType *p_cstr, int p_len); /* hash the string */ - static uint32_t hash(const CharType *p_cstr); /* hash the string */ + Char16String utf16() const; + bool parse_utf16(const char16_t *p_utf16, int p_len = -1); //return true on error + static String utf16(const char16_t *p_utf16, int p_len = -1); + + static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const char32_t *p_cstr); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr); /* hash the string */ static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */ static uint32_t hash(const char *p_cstr); /* hash the string */ uint32_t hash() const; /* hash the string */ @@ -348,7 +431,7 @@ public: /** * The constructors must not depend on other overloads */ - /* String(CharType p_char);*/ + /* String(char32_t p_char);*/ _FORCE_INLINE_ String() {} _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } @@ -358,14 +441,20 @@ public: } String(const char *p_str); - String(const CharType *p_str, int p_clip_to_len = -1); + String(const wchar_t *p_str); + String(const char32_t *p_str); + String(const char *p_str, int p_clip_to_len); + String(const wchar_t *p_str, int p_clip_to_len); + String(const char32_t *p_str, int p_clip_to_len); String(const StrRange &p_range); }; bool operator==(const char *p_chr, const String &p_str); +bool operator==(const wchar_t *p_chr, const String &p_str); String operator+(const char *p_chr, const String &p_str); -String operator+(CharType p_chr, const String &p_str); +String operator+(const wchar_t *p_chr, const String &p_str); +String operator+(char32_t p_chr, const String &p_str); String itos(int64_t p_val); String uitos(uint64_t p_val); @@ -387,15 +476,18 @@ struct NaturalNoCaseComparator { template _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { while (true) { - if (*l_ptr == 0 && *r_ptr == 0) { + const char32_t l = *l_ptr; + const char32_t r = *r_ptr; + + if (l == 0 && r == 0) { return false; - } else if (*l_ptr == 0) { + } else if (l == 0) { return true; - } else if (*r_ptr == 0) { + } else if (r == 0) { return false; - } else if (*l_ptr < *r_ptr) { + } else if (l < r) { return true; - } else if (*l_ptr > *r_ptr) { + } else if (l > r) { return false; } @@ -432,7 +524,7 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St String RTR(const String &p_text, const String &p_context = ""); String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); -bool is_symbol(CharType c); +bool is_symbol(char32_t c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); #endif // USTRING_H diff --git a/core/variant.cpp b/core/variant.cpp index c19ce79e64..181ced0f32 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -1558,7 +1558,7 @@ Variant::operator unsigned char() const { } } -Variant::operator CharType() const { +Variant::operator char32_t() const { return operator unsigned int(); } @@ -2445,7 +2445,7 @@ Variant::Variant(const char *const p_cstring) { memnew_placement(_data._mem, String((const char *)p_cstring)); } -Variant::Variant(const CharType *p_wstring) { +Variant::Variant(const char32_t *p_wstring) { type = STRING; memnew_placement(_data._mem, String(p_wstring)); } diff --git a/core/variant.h b/core/variant.h index 27a709b473..112003a7ae 100644 --- a/core/variant.h +++ b/core/variant.h @@ -246,7 +246,7 @@ public: operator ObjectID() const; - operator CharType() const; + operator char32_t() const; operator float() const; operator double() const; operator String() const; @@ -323,7 +323,7 @@ public: Variant(const String &p_string); Variant(const StringName &p_string); Variant(const char *const p_cstring); - Variant(const CharType *p_wstring); + Variant(const char32_t *p_wstring); Variant(const Vector2 &p_vector2); Variant(const Vector2i &p_vector2i); Variant(const Rect2 &p_rect2); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 91af127d32..7a1fdbaafe 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -239,6 +239,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(String, casecmp_to); VCALL_LOCALMEM1R(String, nocasecmp_to); + VCALL_LOCALMEM1R(String, naturalnocasecmp_to); VCALL_LOCALMEM0R(String, length); VCALL_LOCALMEM3R(String, count); VCALL_LOCALMEM3R(String, countn); @@ -311,6 +312,8 @@ struct _VariantCall { VCALL_LOCALMEM0R(String, to_int); VCALL_LOCALMEM0R(String, to_float); VCALL_LOCALMEM0R(String, hex_to_int); + VCALL_LOCALMEM2R(String, lpad); + VCALL_LOCALMEM2R(String, rpad); VCALL_LOCALMEM1R(String, pad_decimals); VCALL_LOCALMEM1R(String, pad_zeros); VCALL_LOCALMEM1R(String, trim_prefix); @@ -350,6 +353,39 @@ struct _VariantCall { r_ret = retval; } + static void _call_String_to_utf16(Variant &r_ret, Variant &p_self, const Variant **p_args) { + String *s = reinterpret_cast(p_self._data._mem); + if (s->empty()) { + r_ret = PackedByteArray(); + return; + } + Char16String charstr = s->utf16(); + + PackedByteArray retval; + size_t len = charstr.length() * 2; + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, (const void *)charstr.ptr(), len); + + r_ret = retval; + } + + static void _call_String_to_utf32(Variant &r_ret, Variant &p_self, const Variant **p_args) { + String *s = reinterpret_cast(p_self._data._mem); + if (s->empty()) { + r_ret = PackedByteArray(); + return; + } + + PackedByteArray retval; + size_t len = s->length() * 4; + retval.resize(len); + uint8_t *w = retval.ptrw(); + copymem(w, (const void *)s->ptr(), len); + + r_ret = retval; + } + VCALL_LOCALMEM1R(Vector2, distance_to); VCALL_LOCALMEM1R(Vector2, distance_squared_to); VCALL_LOCALMEM0R(Vector2, length); @@ -618,6 +654,26 @@ struct _VariantCall { r_ret = s; } + static void _call_PackedByteArray_get_string_from_utf16(Variant &r_ret, Variant &p_self, const Variant **p_args) { + PackedByteArray *ba = reinterpret_cast(p_self._data._mem); + String s; + if (ba->size() > 0) { + const uint8_t *r = ba->ptr(); + s.parse_utf16((const char16_t *)r, ba->size() / 2); + } + r_ret = s; + } + + static void _call_PackedByteArray_get_string_from_utf32(Variant &r_ret, Variant &p_self, const Variant **p_args) { + PackedByteArray *ba = reinterpret_cast(p_self._data._mem); + String s; + if (ba->size() > 0) { + const uint8_t *r = ba->ptr(); + s = String((const char32_t *)r, ba->size() / 4); + } + r_ret = s; + } + static void _call_PackedByteArray_compress(Variant &r_ret, Variant &p_self, const Variant **p_args) { PackedByteArray *ba = reinterpret_cast(p_self._data._mem); PackedByteArray compressed; @@ -1789,6 +1845,7 @@ void register_variant_methods() { /* STRING */ ADDFUNC1R(STRING, INT, String, casecmp_to, STRING, "to", varray()); 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)); @@ -1867,6 +1924,8 @@ void register_variant_methods() { ADDFUNC0R(STRING, INT, String, to_int, varray()); ADDFUNC0R(STRING, FLOAT, String, to_float, varray()); ADDFUNC0R(STRING, INT, String, hex_to_int, varray()); + ADDFUNC2R(STRING, STRING, String, lpad, INT, "min_length", STRING, "character", varray(" ")); + ADDFUNC2R(STRING, STRING, String, rpad, INT, "min_length", STRING, "character", varray(" ")); ADDFUNC1R(STRING, STRING, String, pad_decimals, INT, "digits", varray()); ADDFUNC1R(STRING, STRING, String, pad_zeros, INT, "digits", varray()); ADDFUNC1R(STRING, STRING, String, trim_prefix, STRING, "prefix", varray()); @@ -1874,6 +1933,8 @@ void register_variant_methods() { ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_ascii, varray()); ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_utf8, varray()); + ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_utf16, varray()); + ADDFUNC0R(STRING, PACKED_BYTE_ARRAY, String, to_utf32, varray()); ADDFUNC0R(VECTOR2, FLOAT, Vector2, angle, varray()); ADDFUNC1R(VECTOR2, FLOAT, Vector2, angle_to, VECTOR2, "to", varray()); @@ -2109,6 +2170,8 @@ void register_variant_methods() { ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_ascii, varray()); ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_utf8, varray()); + ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_utf16, varray()); + ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, get_string_from_utf32, varray()); ADDFUNC0R(PACKED_BYTE_ARRAY, STRING, PackedByteArray, hex_encode, varray()); ADDFUNC1R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, compress, INT, "compression_mode", varray(0)); ADDFUNC2R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0)); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 0cb2fe29a1..95b488230d 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -4215,7 +4215,7 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & int split = csize / 2; for (int i = 0; i < csize; i++) { - CharType chr = ' '; + char32_t chr = ' '; if (i < split) { if (i < sa.length()) { diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 74f4f32c0e..3c4fed68fb 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -35,7 +35,7 @@ #include "core/os/keyboard.h" #include "core/string_buffer.h" -CharType VariantParser::StreamFile::get_char() { +char32_t VariantParser::StreamFile::get_char() { return f->get_8(); } @@ -47,7 +47,7 @@ bool VariantParser::StreamFile::is_eof() const { return f->eof_reached(); } -CharType VariantParser::StreamString::get_char() { +char32_t VariantParser::StreamString::get_char() { if (pos > s.length()) { return 0; } else if (pos == s.length()) { @@ -94,7 +94,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri bool string_name = false; while (true) { - CharType cchar; + char32_t cchar; if (p_stream->saved) { cchar = p_stream->saved; p_stream->saved = 0; @@ -145,7 +145,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } case ';': { while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; @@ -173,7 +173,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri StringBuffer<> color_str; color_str += '#'; while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; @@ -204,7 +204,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri case '"': { String str; while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (ch == 0) { r_err_str = "Unterminated String"; @@ -214,13 +214,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri break; } else if (ch == '\\') { //escaped characters... - CharType next = p_stream->get_char(); + char32_t next = p_stream->get_char(); if (next == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -241,7 +241,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri case 'u': { //hex number for (int j = 0; j < 4; j++) { - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (c == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; @@ -252,7 +252,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -321,7 +321,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri cchar = p_stream->get_char(); } - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; @@ -421,7 +421,7 @@ Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector &strings, String accum; while (true) { - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (p_stream->is_eof()) { r_err_str = "Unexpected EOF while parsing old-style project.godot construct"; @@ -1206,7 +1206,7 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin r_tag.fields.clear(); while (true) { - CharType c = p_stream->get_char(); + char32_t c = p_stream->get_char(); if (p_stream->is_eof()) { r_err_str = "Unexpected EOF while parsing simple tag"; return ERR_PARSE_ERROR; @@ -1305,7 +1305,7 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r String what; while (true) { - CharType c; + char32_t c; if (p_stream->saved) { c = p_stream->saved; p_stream->saved = 0; @@ -1320,7 +1320,7 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r if (c == ';') { //comment while (true) { - CharType ch = p_stream->get_char(); + char32_t ch = p_stream->get_char(); if (p_stream->is_eof()) { return ERR_FILE_EOF; } diff --git a/core/variant_parser.h b/core/variant_parser.h index b55d7b2df0..12329e2db6 100644 --- a/core/variant_parser.h +++ b/core/variant_parser.h @@ -38,11 +38,11 @@ class VariantParser { public: struct Stream { - virtual CharType get_char() = 0; + virtual char32_t get_char() = 0; virtual bool is_utf8() const = 0; virtual bool is_eof() const = 0; - CharType saved = 0; + char32_t saved = 0; Stream() {} virtual ~Stream() {} @@ -51,7 +51,7 @@ public: struct StreamFile : public Stream { FileAccess *f = nullptr; - virtual CharType get_char(); + virtual char32_t get_char(); virtual bool is_utf8() const; virtual bool is_eof() const; @@ -62,7 +62,7 @@ public: String s; int pos = 0; - virtual CharType get_char(); + virtual char32_t get_char(); virtual bool is_utf8() const; virtual bool is_eof() const; diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml index 08f8558881..b1c4e54854 100644 --- a/doc/classes/PackedByteArray.xml +++ b/doc/classes/PackedByteArray.xml @@ -68,14 +68,28 @@ - Returns a copy of the array's contents as [String]. Fast alternative to [method get_string_from_utf8] if the content is ASCII-only. Unlike the UTF-8 function this function maps every byte to a character in the array. Multibyte sequences will not be interpreted correctly. For parsing user input always use [method get_string_from_utf8]. + Converts ASCII/Latin-1 encoded array to [String]. Fast alternative to [method get_string_from_utf8] if the content is ASCII/Latin-1 only. Unlike the UTF-8 function this function maps every byte to a character in the array. Multibyte sequences will not be interpreted correctly. For parsing user input always use [method get_string_from_utf8]. + + + + + + + Converts UTF-16 encoded array to [String]. If the BOM is missing, system endianness is assumed. Returns empty string if source array is not vaild UTF-16 string. + + + + + + + Converts UTF-32 encoded array to [String]. System endianness is assumed. Returns empty string if source array is not vaild UTF-32 string. - Returns a copy of the array's contents as [String]. Slower than [method get_string_from_ascii] but supports UTF-8 encoded data. Use this function if you are unsure about the source of the data. For user input this function should always be preferred. + Converts UTF-8 encoded array to [String]. Slower than [method get_string_from_ascii] but supports UTF-8 encoded data. Use this function if you are unsure about the source of the data. For user input this function should always be preferred. Returns empty string if source array is not vaild UTF-8 string. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index e134ae03e0..40fff25fc4 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -654,6 +654,17 @@ Returns the string's amount of characters. + + + + + + + + + Formats a string to be at least [code]min_length[/code] long by adding [code]character[/code]s to the left of the string. + + @@ -695,6 +706,15 @@ Returns the MD5 hash of the string as a string. + + + + + + + Performs a case-insensitive natural order comparison to another string. Returns [code]-1[/code] if less than, [code]+1[/code] if greater than, or [code]0[/code] if equal. + + @@ -816,6 +836,17 @@ Returns the right side of the string from a given position. + + + + + + + + + Formats a string to be at least [code]min_length[/code] long by adding [code]character[/code]s to the right of the string. + + @@ -953,7 +984,7 @@ - Converts the String (which is a character array) to [PackedByteArray] (which is an array of bytes). The conversion is faster compared to [method to_utf8], as this method assumes that all the characters in the String are ASCII characters. + Converts the String (which is a character array) to ASCII/Latin-1 encoded [PackedByteArray] (which is an array of bytes). The conversion is faster compared to [method to_utf8], as this method assumes that all the characters in the String are ASCII/Latin-1 characters, unsupported characters are replaced with spaces. @@ -984,11 +1015,25 @@ Returns the string converted to uppercase. + + + + + Converts the String (which is an array of characters) to UTF-16 encoded [PackedByteArray] (which is an array of bytes). + + + + + + + Converts the String (which is an array of characters) to UTF-32 encoded [PackedByteArray] (which is an array of bytes). + + - Converts the String (which is an array of characters) to [PackedByteArray] (which is an array of bytes). The conversion is a bit slower than [method to_ascii], but supports all UTF-8 characters. Therefore, you should prefer this function over [method to_ascii]. + Converts the String (which is an array of characters) to UTF-8 encode [PackedByteArray] (which is an array of bytes). The conversion is a bit slower than [method to_ascii], but supports all UTF-8 characters. Therefore, you should prefer this function over [method to_ascii]. diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 06bad9f385..51c657007c 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -78,7 +78,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { path_src = p_path; path = fix_path(p_path); - //printf("opening %ls, %i\n", path.c_str(), Memory::get_static_mem_usage()); + //printf("opening %s, %i\n", path.utf8().get_data(), Memory::get_static_mem_usage()); ERR_FAIL_COND_V_MSG(f, ERR_ALREADY_IN_USE, "File is already in use."); const char *mode_string; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index fb890491a4..9fe3f7e6c0 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3650,7 +3650,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(VectorgetQualifier().layoutPushConstant) { uint32_t len = reflection.size; if (push_constant.push_constant_size != 0 && push_constant.push_constant_size != len) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' push constants for different stages should all be the same size."; + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' push constants for different stages should all be the same size."; return false; } push_constant.push_constant_size = len; @@ -3714,7 +3714,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(VectorgetQualifier().storage) + ")."; + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' is of unsupported block type: (" + itos(reflection.getType()->getQualifier().storage) + ")."; } return false; } @@ -3743,7 +3743,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(VectorgetQualifier().hasBinding()) { if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' lacks a binding number."; + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' lacks a binding number."; } return false; } @@ -3760,14 +3760,14 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector= MAX_UNIFORM_SETS) { if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."; + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."; } return false; } if (set >= limits.maxBoundDescriptorSets) { if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."; + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."; } return false; } @@ -3781,7 +3781,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vectorh = FindFirstFileExW((current_dir + "\\*").c_str(), FindExInfoStandard, &p->fu, FindExSearchNameMatch, nullptr, 0); + p->h = FindFirstFileExW((LPCWSTR)(String(current_dir + "\\*").utf16().get_data()), FindExInfoStandard, &p->fu, FindExSearchNameMatch, nullptr, 0); if (p->h == INVALID_HANDLE_VALUE) { return ERR_CANT_OPEN; @@ -75,13 +75,14 @@ Error DirAccessWindows::list_dir_begin() { } String DirAccessWindows::get_next() { - if (p->h == INVALID_HANDLE_VALUE) + if (p->h == INVALID_HANDLE_VALUE) { return ""; + } _cisdir = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); _cishidden = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN); - String name = p->fu.cFileName; + String name = String::utf16((const char16_t *)(p->fu.cFileName)); if (FindNextFileW(p->h, &p->fu) == 0) { FindClose(p->h); @@ -111,8 +112,9 @@ int DirAccessWindows::get_drive_count() { } String DirAccessWindows::get_drive(int p_drive) { - if (p_drive < 0 || p_drive >= drive_count) + if (p_drive < 0 || p_drive >= drive_count) { return ""; + } return String::chr(drives[p_drive]) + ":"; } @@ -122,18 +124,17 @@ Error DirAccessWindows::change_dir(String p_dir) { p_dir = fix_path(p_dir); - wchar_t real_current_dir_name[2048]; + WCHAR real_current_dir_name[2048]; GetCurrentDirectoryW(2048, real_current_dir_name); - String prev_dir = real_current_dir_name; + String prev_dir = String::utf16((const char16_t *)real_current_dir_name); - SetCurrentDirectoryW(current_dir.c_str()); - bool worked = (SetCurrentDirectoryW(p_dir.c_str()) != 0); + SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data())); + bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0); String base = _get_root_path(); if (base != "") { GetCurrentDirectoryW(2048, real_current_dir_name); - String new_dir; - new_dir = String(real_current_dir_name).replace("\\", "/"); + String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/"); if (!new_dir.begins_with(base)) { worked = false; } @@ -141,13 +142,11 @@ Error DirAccessWindows::change_dir(String p_dir) { if (worked) { GetCurrentDirectoryW(2048, real_current_dir_name); - current_dir = real_current_dir_name; // TODO, utf8 parser + current_dir = String::utf16((const char16_t *)real_current_dir_name); current_dir = current_dir.replace("\\", "/"); + } - } //else { - - SetCurrentDirectoryW(prev_dir.c_str()); - //} + SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data())); return worked ? OK : ERR_INVALID_PARAMETER; } @@ -156,8 +155,9 @@ Error DirAccessWindows::make_dir(String p_dir) { GLOBAL_LOCK_FUNCTION p_dir = fix_path(p_dir); - if (p_dir.is_rel_path()) + if (p_dir.is_rel_path()) { p_dir = current_dir.plus_file(p_dir); + } p_dir = p_dir.replace("/", "\\"); @@ -167,16 +167,16 @@ Error DirAccessWindows::make_dir(String p_dir) { p_dir = "\\\\?\\" + p_dir; //done according to // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx - success = CreateDirectoryW(p_dir.c_str(), nullptr); + success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr); err = GetLastError(); if (success) { return OK; - }; + } if (err == ERROR_ALREADY_EXISTS || err == ERROR_ACCESS_DENIED) { return ERR_ALREADY_EXISTS; - }; + } return ERR_CANT_CREATE; } @@ -185,12 +185,11 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) { String base = _get_root_path(); if (base != "") { String bd = current_dir.replace("\\", "/").replace_first(base, ""); - if (bd.begins_with("/")) + if (bd.begins_with("/")) { return _get_root_string() + bd.substr(1, bd.length()); - else + } else { return _get_root_string() + bd; - - } else { + } } if (p_include_drive) { @@ -209,20 +208,18 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) { bool DirAccessWindows::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION - if (!p_file.is_abs_path()) + if (!p_file.is_abs_path()) { p_file = get_current_dir().plus_file(p_file); + } p_file = fix_path(p_file); - //p_file.replace("/","\\"); - - //WIN32_FILE_ATTRIBUTE_DATA fileInfo; - DWORD fileAttr; - fileAttr = GetFileAttributesW(p_file.c_str()); - if (INVALID_FILE_ATTRIBUTES == fileAttr) + fileAttr = GetFileAttributesW((LPCWSTR)(p_file.utf16().get_data())); + if (INVALID_FILE_ATTRIBUTES == fileAttr) { return false; + } return !(fileAttr & FILE_ATTRIBUTE_DIRECTORY); } @@ -230,31 +227,30 @@ bool DirAccessWindows::file_exists(String p_file) { bool DirAccessWindows::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION - if (p_dir.is_rel_path()) + if (p_dir.is_rel_path()) { p_dir = get_current_dir().plus_file(p_dir); + } p_dir = fix_path(p_dir); - //p_dir.replace("/","\\"); - - //WIN32_FILE_ATTRIBUTE_DATA fileInfo; - DWORD fileAttr; - - fileAttr = GetFileAttributesW(p_dir.c_str()); - if (INVALID_FILE_ATTRIBUTES == fileAttr) + fileAttr = GetFileAttributesW((LPCWSTR)(p_dir.utf16().get_data())); + if (INVALID_FILE_ATTRIBUTES == fileAttr) { return false; + } return (fileAttr & FILE_ATTRIBUTE_DIRECTORY); } Error DirAccessWindows::rename(String p_path, String p_new_path) { - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { p_path = get_current_dir().plus_file(p_path); + } p_path = fix_path(p_path); - if (p_new_path.is_rel_path()) + if (p_new_path.is_rel_path()) { p_new_path = get_current_dir().plus_file(p_new_path); + } p_new_path = fix_path(p_new_path); @@ -262,16 +258,16 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { if (p_path.to_lower() == p_new_path.to_lower()) { WCHAR tmpfile[MAX_PATH]; - if (!GetTempFileNameW(fix_path(get_current_dir()).c_str(), nullptr, 0, tmpfile)) { + if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), nullptr, 0, tmpfile)) { return FAILED; } - if (!::ReplaceFileW(tmpfile, p_path.c_str(), nullptr, 0, nullptr, nullptr)) { + if (!::ReplaceFileW(tmpfile, (LPCWSTR)(p_path.utf16().get_data()), nullptr, 0, nullptr, nullptr)) { DeleteFileW(tmpfile); return FAILED; } - return ::_wrename(tmpfile, p_new_path.c_str()) == 0 ? OK : FAILED; + return ::_wrename(tmpfile, (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; } else { if (file_exists(p_new_path)) { @@ -280,60 +276,60 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { } } - return ::_wrename(p_path.c_str(), p_new_path.c_str()) == 0 ? OK : FAILED; + return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; } } Error DirAccessWindows::remove(String p_path) { - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { p_path = get_current_dir().plus_file(p_path); + } p_path = fix_path(p_path); DWORD fileAttr; - fileAttr = GetFileAttributesW(p_path.c_str()); - if (INVALID_FILE_ATTRIBUTES == fileAttr) + fileAttr = GetFileAttributesW((LPCWSTR)(p_path.utf16().get_data())); + if (INVALID_FILE_ATTRIBUTES == fileAttr) { return FAILED; - if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY)) - return ::_wrmdir(p_path.c_str()) == 0 ? OK : FAILED; - else - return ::_wunlink(p_path.c_str()) == 0 ? OK : FAILED; + } + if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY)) { + return ::_wrmdir((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED; + } else { + return ::_wunlink((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED; + } } /* FileType DirAccessWindows::get_file_type(const String& p_file) const { + WCHAR real_current_dir_name[2048]; + GetCurrentDirectoryW(2048, real_current_dir_name); + String prev_dir = Strong::utf16((const char16_t *)real_current_dir_name); - - wchar_t real_current_dir_name[2048]; - GetCurrentDirectoryW(2048,real_current_dir_name); - String prev_dir=real_current_dir_name; - - bool worked SetCurrentDirectoryW(current_dir.c_str()); + bool worked = SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data())); DWORD attr; if (worked) { - - WIN32_FILE_ATTRIBUTE_DATA fileInfo; - attr = GetFileAttributesExW(p_file.c_str(), GetFileExInfoStandard, &fileInfo); - + WIN32_FILE_ATTRIBUTE_DATA fileInfo; + attr = GetFileAttributesExW((LPCWSTR)(p_file.utf16().get_data()), GetFileExInfoStandard, &fileInfo); } - SetCurrentDirectoryW(prev_dir.c_str()); + SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data())); - if (!worked) + if (!worked) { return FILE_TYPE_NONE; + } - - return (attr&FILE_ATTRIBUTE_DIRECTORY)?FILE_TYPE_ + return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FILE_TYPE_ } */ size_t DirAccessWindows::get_space_left() { uint64_t bytes = 0; - if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) + if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) { return 0; + } //this is either 0 or a value in bytes. return (size_t)bytes; @@ -352,7 +348,7 @@ String DirAccessWindows::get_filesystem_type() const { DWORD dwMaxFileNameLength = 0; DWORD dwFileSystemFlags = 0; - if (::GetVolumeInformationW(unit.c_str(), + if (::GetVolumeInformationW((LPCWSTR)(unit.utf16().get_data()), szVolumeName, sizeof(szVolumeName), &dwSerialNumber, @@ -360,7 +356,7 @@ String DirAccessWindows::get_filesystem_type() const { &dwFileSystemFlags, szFileSystemName, sizeof(szFileSystemName)) == TRUE) { - return String(szFileSystemName); + return String::utf16((const char16_t *)szFileSystemName); } ERR_FAIL_V(""); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 50366d0b2d..dd86061ea7 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -59,29 +59,32 @@ void FileAccessWindows::check_errors() const { Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { path_src = p_path; path = fix_path(p_path); - if (f) + if (f) { close(); + } - const wchar_t *mode_string; + const WCHAR *mode_string; - if (p_mode_flags == READ) + if (p_mode_flags == READ) { mode_string = L"rb"; - else if (p_mode_flags == WRITE) + } else if (p_mode_flags == WRITE) { mode_string = L"wb"; - else if (p_mode_flags == READ_WRITE) + } else if (p_mode_flags == READ_WRITE) { mode_string = L"rb+"; - else if (p_mode_flags == WRITE_READ) + } else if (p_mode_flags == WRITE_READ) { mode_string = L"wb+"; - else + } else { return ERR_INVALID_PARAMETER; + } /* pretty much every implementation that uses fopen as primary backend supports utf8 encoding */ struct _stat st; - if (_wstat(path.c_str(), &st) == 0) { - if (!S_ISREG(st.st_mode)) + if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) { + if (!S_ISREG(st.st_mode)) { return ERR_FILE_CANT_OPEN; + } }; #ifdef TOOLS_ENABLED @@ -91,9 +94,9 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { // platforms). if (p_mode_flags == READ) { WIN32_FIND_DATAW d; - HANDLE f = FindFirstFileW(path.c_str(), &d); + HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); if (f != INVALID_HANDLE_VALUE) { - String fname = d.cFileName; + String fname = String::utf16((const char16_t *)(d.cFileName)); if (fname != String()) { String base_file = path.get_file(); if (base_file != fname && base_file.findn(fname) == 0) { @@ -110,7 +113,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { path = path + ".tmp"; } - errno_t errcode = _wfopen_s(&f, path.c_str(), mode_string); + errno_t errcode = _wfopen_s(&f, (LPCWSTR)(path.utf16().get_data()), mode_string); if (f == nullptr) { switch (errcode) { @@ -130,8 +133,9 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { } void FileAccessWindows::close() { - if (!f) + if (!f) { return; + } fclose(f); f = nullptr; @@ -148,16 +152,16 @@ void FileAccessWindows::close() { // UWP has no PathFileExists, so we check attributes instead DWORD fileAttr; - fileAttr = GetFileAttributesW(save_path.c_str()); + fileAttr = GetFileAttributesW((LPCWSTR)(save_path.utf16().get_data())); if (INVALID_FILE_ATTRIBUTES == fileAttr) { #else - if (!PathFileExistsW(save_path.c_str())) { + if (!PathFileExistsW((LPCWSTR)(save_path.utf16().get_data()))) { #endif //creating new file - rename_error = _wrename((save_path + ".tmp").c_str(), save_path.c_str()) != 0; + rename_error = _wrename((LPCWSTR)((save_path + ".tmp").utf16().get_data()), (LPCWSTR)(save_path.utf16().get_data())) != 0; } else { //atomic replace for existing file - rename_error = !ReplaceFileW(save_path.c_str(), (save_path + ".tmp").c_str(), nullptr, 2 | 4, nullptr, nullptr); + rename_error = !ReplaceFileW((LPCWSTR)(save_path.utf16().get_data()), (LPCWSTR)((save_path + ".tmp").utf16().get_data()), nullptr, 2 | 4, nullptr, nullptr); } if (rename_error) { attempts--; @@ -192,15 +196,17 @@ bool FileAccessWindows::is_open() const { void FileAccessWindows::seek(size_t p_position) { ERR_FAIL_COND(!f); last_error = OK; - if (fseek(f, p_position, SEEK_SET)) + if (fseek(f, p_position, SEEK_SET)) { check_errors(); + } prev_op = 0; } void FileAccessWindows::seek_end(int64_t p_position) { ERR_FAIL_COND(!f); - if (fseek(f, p_position, SEEK_END)) + if (fseek(f, p_position, SEEK_END)) { check_errors(); + } prev_op = 0; } @@ -209,7 +215,7 @@ size_t FileAccessWindows::get_position() const { aux_position = ftell(f); if (!aux_position) { check_errors(); - }; + } return aux_position; } @@ -241,7 +247,7 @@ uint8_t FileAccessWindows::get_8() const { if (fread(&b, 1, 1, f) == 0) { check_errors(); b = '\0'; - }; + } return b; } @@ -266,8 +272,9 @@ Error FileAccessWindows::get_error() const { void FileAccessWindows::flush() { ERR_FAIL_COND(!f); fflush(f); - if (prev_op == WRITE) + if (prev_op == WRITE) { prev_op = 0; + } } void FileAccessWindows::store_8(uint8_t p_dest) { @@ -298,9 +305,9 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) { bool FileAccessWindows::file_exists(const String &p_name) { FILE *g; - //printf("opening file %s\n", p_fname.c_str()); + //printf("opening file %s\n", p_fname.utf8().get_data()); String filename = fix_path(p_name); - _wfopen_s(&g, filename.c_str(), L"rb"); + _wfopen_s(&g, (LPCWSTR)(filename.utf16().get_data()), L"rb"); if (g == nullptr) { return false; } else { @@ -315,7 +322,7 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { file = file.substr(0, file.length() - 1); struct _stat st; - int rv = _wstat(file.c_str(), &st); + int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); if (rv == 0) { return st.st_mtime; diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index d3dff3f375..20d29d47f4 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -714,7 +714,7 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { const String &signalname = signal; String midname = selectedNode->get_name(); for (int i = 0; i < midname.length(); i++) { //TODO: Regex filter may be cleaner. - CharType c = midname[i]; + char32_t c = midname[i]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { if (c == ' ') { // Replace spaces with underlines. diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index bce34db740..30aebd2b1f 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -244,7 +244,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview class_desc->push_cell(); class_desc->push_align(RichTextLabel::ALIGN_RIGHT); } else { - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); } @@ -761,7 +761,7 @@ void EditorHelp::_update_doc() { signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; //gets overridden if description class_desc->push_font(doc_code_font); // monofont class_desc->push_color(headline_color); - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); _add_text(cd.signals[i].name); class_desc->pop(); @@ -876,7 +876,7 @@ void EditorHelp::_update_doc() { class_desc->push_font(doc_code_font); class_desc->push_color(headline_color); - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); _add_text(enum_list[i].name); class_desc->pop(); @@ -890,7 +890,7 @@ void EditorHelp::_update_doc() { if (enum_list[i].description != "") { class_desc->push_font(doc_font); class_desc->push_color(comment_color); - static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; + static const char32_t dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; class_desc->add_text(String(dash)); _add_text(DTR(enum_list[i].description)); class_desc->pop(); @@ -937,12 +937,12 @@ void EditorHelp::_update_doc() { Vector color = stripped.split_floats(","); if (color.size() >= 3) { class_desc->push_color(Color(color[0], color[1], color[2])); - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); class_desc->pop(); } } else { - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); } @@ -960,7 +960,7 @@ void EditorHelp::_update_doc() { if (constants[i].description != "") { class_desc->push_font(doc_font); class_desc->push_color(comment_color); - static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; + static const char32_t dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; class_desc->add_text(String(dash)); _add_text(DTR(constants[i].description)); class_desc->pop(); @@ -1002,7 +1002,7 @@ void EditorHelp::_update_doc() { class_desc->push_cell(); class_desc->push_font(doc_code_font); - static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; class_desc->add_text(String(prefix)); _add_type(cd.properties[i].type, cd.properties[i].enumeration); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index b49c50fa31..7fada633c9 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -192,9 +192,9 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L String exec = OS::get_singleton()->get_executable_path(); - printf("Running: %ls", exec.c_str()); + printf("Running: %s", exec.utf8().get_data()); for (List::Element *E = args.front(); E; E = E->next()) { - printf(" %ls", E->get().c_str()); + printf(" %s", E->get().utf8().get_data()); }; printf("\n"); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index bd4bb57dcf..c2ccbdb08c 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -54,7 +54,7 @@ inline void pop_back(T &container) { } // TODO Copied from TextEdit private, would be nice to extract it in a single place -static bool is_text_char(CharType c) { +static bool is_text_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } @@ -854,7 +854,7 @@ public: String get_line(FileAccess *f) { _line_buffer.clear(); - CharType c = f->get_8(); + char32_t c = f->get_8(); while (!f->eof_reached()) { if (c == '\n') { diff --git a/editor/input_map_editor.cpp b/editor/input_map_editor.cpp index 52cf9c1869..c67e16d371 100644 --- a/editor/input_map_editor.cpp +++ b/editor/input_map_editor.cpp @@ -100,7 +100,7 @@ void InputMapEditor::_notification(int p_what) { } static bool _validate_action_name(const String &p_name) { - const CharType *cstr = p_name.c_str(); + const char32_t *cstr = p_name.get_data(); for (int i = 0; cstr[i]; i++) { if (cstr[i] == '/' || cstr[i] == ':' || cstr[i] == '"' || cstr[i] == '=' || cstr[i] == '\\' || cstr[i] < 32) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 3f9f159d7f..9427f82f9e 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -6256,7 +6256,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); + accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.get_data())); accept->popup_centered(); } } diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 2889cb50a0..3cf4dc5ac8 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -466,7 +466,7 @@ EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { /////////////////////////////////////////////////////////////////////////// -static bool _is_text_char(CharType c) { +static bool _is_text_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } @@ -525,7 +525,7 @@ Ref EditorScriptPreviewPlugin::generate(const RES &p_from, const Size bool prev_is_text = false; bool in_keyword = false; for (int i = 0; i < code.length(); i++) { - CharType c = code[i]; + char32_t c = code[i]; if (c > 32) { if (col < thumbnail_size) { Color color = text_color; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 21a75c2f5d..952487c13c 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3729,7 +3729,7 @@ void Node3DEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); + accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.get_data())); accept->popup_centered(); } } diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 26c40b625c..1fa19f4ff5 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -40,9 +40,10 @@ extern "C" { #endif +static_assert(sizeof(godot_char16_string) == sizeof(Char16String), "Char16String size mismatch"); static_assert(sizeof(godot_char_string) == sizeof(CharString), "CharString size mismatch"); static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch"); -static_assert(sizeof(godot_char_type) == sizeof(CharType), "CharType size mismatch"); +static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch"); godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs) { const CharString *cs = (const CharString *)p_cs; @@ -62,6 +63,24 @@ void GDAPI godot_char_string_destroy(godot_char_string *p_cs) { cs->~CharString(); } +godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs) { + const Char16String *cs = (const Char16String *)p_cs; + + return cs->length(); +} + +const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs) { + const Char16String *cs = (const Char16String *)p_cs; + + return cs->get_data(); +} + +void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs) { + Char16String *cs = (Char16String *)p_cs; + + cs->~Char16String(); +} + void GDAPI godot_string_new(godot_string *r_dest) { String *dest = (String *)r_dest; memnew_placement(dest, String); @@ -70,27 +89,97 @@ void GDAPI godot_string_new(godot_string *r_dest) { void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src) { String *dest = (String *)r_dest; const String *src = (const String *)p_src; - memnew_placement(dest, String(*src)); + memnew_placement(dest, String); + *dest = String(*src); } -void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size) { +void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents) { String *dest = (String *)r_dest; - memnew_placement(dest, String(p_contents, p_size)); + memnew_placement(dest, String); + *dest = String(p_contents); } -const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { +void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents); +} + +void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents); +} + +void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); +} + +void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); + } +} + +void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); +} + +void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents, p_size); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); + } +} + +const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { String *self = (String *)p_self; return &(self->operator[](p_idx)); } -wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) { +godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) { const String *self = (const String *)p_self; return self->operator[](p_idx); } -const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self) { +const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self) { const String *self = (const String *)p_self; - return self->c_str(); + return self->get_data(); } godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b) { @@ -162,22 +251,14 @@ godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, return self->begins_with(p_char_array); } -godot_array GDAPI godot_string_bigrams(const godot_string *p_self) { +godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector return_value = self->bigrams(); - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->bigrams())); + return ret; }; -godot_string GDAPI godot_string_chr(wchar_t p_character) { +godot_string GDAPI godot_string_chr(godot_char_type p_character) { godot_string result; memnew_placement(&result, String(String::chr(p_character))); @@ -191,88 +272,73 @@ godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_ return self->ends_with(*string); } -godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) { +godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + + return self->ends_with(p_char_array); +} + +godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) { + const String *self = (const String *)p_self; + const String *what = (const String *)p_what; return self->count(*what, p_from, p_to); } -godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) { +godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->countn(*what, p_from, p_to); } -godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->find(*what); } -godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->find(*what, p_from); } -godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys) { +godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys) { const String *self = (const String *)p_self; - - Vector keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - - return self->findmk(keys); + const Vector *keys = (const Vector *)p_keys; + return self->findmk(*keys); } -godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from) { +godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from) { const String *self = (const String *)p_self; - - Vector keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - - return self->findmk(keys, p_from); + const Vector *keys = (const Vector *)p_keys; + return self->findmk(*keys, p_from); } -godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key) { +godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key) { const String *self = (const String *)p_self; - - Vector keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - + const Vector *keys = (const Vector *)p_keys; int key; - int ret = self->findmk(keys, p_from, &key); + int ret = self->findmk(*keys, p_from, &key); if (r_key) { *r_key = key; } return ret; } -godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->findn(*what); } -godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->findn(*what, p_from); } @@ -303,21 +369,9 @@ godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot return result; } -godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) { +godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string) { const String *self = (const String *)p_self; - - return self->hex_to_int(); -} - -godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self) { - const String *self = (const String *)p_self; - - return self->hex_to_int(true); -} - -godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string) { - const String *self = (const String *)p_self; - String *content = (String *)&p_string; + const String *content = (const String *)p_string; godot_string result; memnew_placement(&result, String(self->insert(p_at_pos, *content))); @@ -440,58 +494,58 @@ godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int return result; } -godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replace(*key, *with))); return result; } -godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replacen(*key, *with))); return result; } -godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfind(*what); } -godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfindn(*what); } -godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfind(*what, p_from); } -godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfindn(*what, p_from); } -godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replace_first(*key, *with))); @@ -541,18 +595,18 @@ godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_f return result; } -double GDAPI godot_string_to_float(const godot_string *p_self) { - const String *self = (const String *)p_self; - - return self->to_float(); -} - godot_int GDAPI godot_string_to_int(const godot_string *p_self) { const String *self = (const String *)p_self; return self->to_int(); } +double GDAPI godot_string_to_float(const godot_string *p_self) { + const String *self = (const String *)p_self; + + return self->to_float(); +} + godot_string GDAPI godot_string_capitalize(const godot_string *p_self) { const String *self = (const String *)p_self; godot_string result; @@ -581,11 +635,15 @@ double GDAPI godot_string_char_to_float(const char *p_what) { return String::to_float(p_what); } +double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end) { + return String::to_float(p_str, r_end); +} + godot_int GDAPI godot_string_char_to_int(const char *p_what) { return String::to_int(p_what); } -int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str) { +godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str) { return String::to_int(p_str); } @@ -593,42 +651,32 @@ godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int return String::to_int(p_what, p_len); } -int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len) { +godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len) { return String::to_int(p_str, p_len); } -int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self) { +godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) { const String *self = (const String *)p_self; return self->hex_to_int(false); } -int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self) { +godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self) { const String *self = (const String *)p_self; return self->hex_to_int(); } -int64_t GDAPI godot_string_to_int64(const godot_string *p_self) { +godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice) { const String *self = (const String *)p_self; - - return self->to_int(); -} - -double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end) { - return String::to_float(p_str, r_end); -} - -godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice) { - const String *self = (const String *)p_self; - String *splitter = (String *)&p_splitter; + const String *splitter = (const String *)p_splitter; godot_string result; memnew_placement(&result, String(self->get_slice(*splitter, p_slice))); return result; } -godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice) { +godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice) { const String *self = (const String *)p_self; godot_string result; memnew_placement(&result, String(self->get_slicec(p_splitter, p_slice))); @@ -636,221 +684,149 @@ godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p return result; } -godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split(*splitter, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->split(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split(*splitter); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->split(*splitter, true))); + return ret; } -godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_floats(*splitter, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->split(*splitter, p_allow_empty, p_maxsplit))); + return ret; } -godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_floats(*splitter); - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->rsplit(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters) { - const String *self = (const String *)p_self; - - Vector splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_floats_mk(splitters, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; -} - -godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) { - const String *self = (const String *)p_self; - - Vector splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_floats_mk(splitters); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; -} - -godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_ints(*splitter, false); - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->rsplit(*splitter, true))); + return ret; } -godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_ints(*splitter); - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->rsplit(*splitter, p_allow_empty, p_maxsplit))); + return ret; } -godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - Vector splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_ints_mk(splitters, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector(self->split_floats(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - Vector splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_ints_mk(splitters); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector(self->split_floats(*splitter, true))); + return ret; } -godot_array GDAPI godot_string_split_spaces(const godot_string *p_self) { +godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) { const String *self = (const String *)p_self; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector return_value = self->split_spaces(); + const Vector *splitters = (const Vector *)p_splitters; - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector(self->split_floats_mk(*splitters, false))); + return ret; } -godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter) { +godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) { const String *self = (const String *)p_self; - String *splitter = (String *)&p_splitter; + const Vector *splitters = (const Vector *)p_splitters; + + godot_packed_float32_array ret; + memnew_placement(&ret, Vector(self->split_floats_mk(*splitters, true))); + return ret; +} + +godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) { + const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; + + godot_packed_int32_array ret; + memnew_placement(&ret, Vector(self->split_ints(*splitter, false))); + return ret; +} + +godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { + const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; + + godot_packed_int32_array ret; + memnew_placement(&ret, Vector(self->split_ints(*splitter, true))); + return ret; +} + +godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) { + const String *self = (const String *)p_self; + const Vector *splitters = (const Vector *)p_splitters; + + godot_packed_int32_array ret; + memnew_placement(&ret, Vector(self->split_ints_mk(*splitters, false))); + return ret; +} + +godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) { + const String *self = (const String *)p_self; + const Vector *splitters = (const Vector *)p_splitters; + + godot_packed_int32_array ret; + memnew_placement(&ret, Vector(self->split_ints_mk(*splitters, true))); + return ret; +} + +godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self) { + const String *self = (const String *)p_self; + + godot_packed_string_array ret; + memnew_placement(&ret, Vector(self->split_spaces())); + return ret; +} + +godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter) { + const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; return self->get_slice_count(*splitter); } -wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char) { +godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char) { return String::char_lowercase(p_char); } -wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char) { +godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char) { return String::char_uppercase(p_char); } @@ -894,7 +870,7 @@ godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos return result; } -wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) { +godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) { const String *self = (const String *)p_self; return self->ord_at(p_idx); @@ -917,6 +893,14 @@ godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_po return result; } +godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count) { + const String *self = (const String *)p_self; + godot_string result; + memnew_placement(&result, String(self->repeat(p_count))); + + return result; +} + godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right) { const String *self = (const String *)p_self; godot_string result; @@ -948,7 +932,7 @@ godot_char_string GDAPI godot_string_ascii(const godot_string *p_self) { return result; } -godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self) { +godot_char_string GDAPI godot_string_latin1(const godot_string *p_self) { const String *self = (const String *)p_self; godot_char_string result; @@ -994,6 +978,42 @@ godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot return result; } +godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self) { + const String *self = (const String *)p_self; + + godot_char16_string result; + + memnew_placement(&result, Char16String(self->utf16())); + + return result; +} + +godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16) { + String *self = (String *)p_self; + + return self->parse_utf16(p_utf16); +} + +godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len) { + String *self = (String *)p_self; + + return self->parse_utf16(p_utf16, p_len); +} + +godot_string GDAPI godot_string_chars_to_utf16(const char16_t *p_utf16) { + godot_string result; + memnew_placement(&result, String(String::utf16(p_utf16))); + + return result; +} + +godot_string GDAPI godot_string_chars_to_utf16_with_len(const char16_t *p_utf16, godot_int p_len) { + godot_string result; + memnew_placement(&result, String(String::utf16(p_utf16, p_len))); + + return result; +} + uint32_t GDAPI godot_string_hash(const godot_string *p_self) { const String *self = (const String *)p_self; @@ -1014,28 +1034,18 @@ uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_ return String::hash(p_cstr, p_len); } -uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str) { +uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str) { return String::hash(p_str); } -uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len) { +uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len) { return String::hash(p_str, p_len); } godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector tmp_result = self->md5_buffer(); - godot_packed_byte_array result; - memnew_placement(&result, PackedByteArray); - PackedByteArray *proxy = (PackedByteArray *)&result; - uint8_t *proxy_writer = proxy->ptrw(); - proxy->resize(tmp_result.size()); - - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } - + memnew_placement(&result, PackedByteArray(self->md5_buffer())); return result; } @@ -1047,20 +1057,25 @@ godot_string GDAPI godot_string_md5_text(const godot_string *p_self) { return result; } +godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_packed_byte_array result; + memnew_placement(&result, PackedByteArray(self->sha1_buffer())); + return result; +} + +godot_string GDAPI godot_string_sha1_text(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_string result; + memnew_placement(&result, String(self->sha1_text())); + + return result; +} + godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector tmp_result = self->sha256_buffer(); - godot_packed_byte_array result; - memnew_placement(&result, PackedByteArray); - PackedByteArray *proxy = (PackedByteArray *)&result; - uint8_t *proxy_writer = proxy->ptrw(); - proxy->resize(tmp_result.size()); - - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } - + memnew_placement(&result, PackedByteArray(self->sha256_buffer())); return result; } @@ -1206,15 +1221,6 @@ godot_string GDAPI godot_string_json_escape(const godot_string *p_self) { return result; } -godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line) { - const String *self = (const String *)p_self; - godot_string result; - String return_value = self->word_wrap(p_chars_per_line); - memnew_placement(&result, String(return_value)); - - return result; -} - godot_string GDAPI godot_string_xml_escape(const godot_string *p_self) { const String *self = (const String *)p_self; godot_string result; @@ -1260,6 +1266,22 @@ godot_string GDAPI godot_string_percent_encode(const godot_string *p_self) { return result; } +godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts) { + const String *self = (const String *)p_self; + const Vector *parts = (const Vector *)p_parts; + godot_string result; + String return_value = self->join(*parts); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self) { + const String *self = (const String *)p_self; + + return self->is_valid_filename(); +} + godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self) { const String *self = (const String *)p_self; @@ -1325,6 +1347,16 @@ godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const go return result; } +godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars) { + const String *self = (const String *)p_self; + String *chars = (String *)p_chars; + godot_string result; + String return_value = self->lstrip(*chars); + memnew_placement(&result, String(return_value)); + + return result; +} + godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) { const String *self = (const String *)p_self; String *chars = (String *)p_chars; @@ -1335,25 +1367,6 @@ godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_s return result; } -godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, - const godot_bool p_allow_empty, const godot_int p_maxsplit) { - const String *self = (const String *)p_self; - String *divisor = (String *)p_divisor; - - godot_packed_string_array result; - memnew_placement(&result, PackedStringArray); - PackedStringArray *proxy = (PackedStringArray *)&result; - String *proxy_writer = proxy->ptrw(); - Vector tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit); - proxy->resize(tmp_result.size()); - - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } - - return result; -} - #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 8ccf44ff1a..82bfbd23de 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -3731,6 +3731,27 @@ ["godot_char_string *", "p_cs"] ] }, + { + "name": "godot_char16_string_length", + "return_type": "godot_int", + "arguments": [ + ["const godot_char16_string *", "p_cs"] + ] + }, + { + "name": "godot_char16_string_get_data", + "return_type": "const char16_t *", + "arguments": [ + ["const godot_char16_string *", "p_cs"] + ] + }, + { + "name": "godot_char16_string_destroy", + "return_type": "void", + "arguments": [ + ["godot_char16_string *", "p_cs"] + ] + }, { "name": "godot_string_new", "return_type": "void", @@ -3747,7 +3768,83 @@ ] }, { - "name": "godot_string_new_with_wide_string", + "name": "godot_string_new_with_latin1_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf8_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf16_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char16_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf32_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char32_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_wide_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const wchar_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_latin1_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf8_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf16_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char16_t *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf32_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char32_t *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_wide_chars_and_len", "return_type": "void", "arguments": [ ["godot_string *", "r_dest"], @@ -3757,7 +3854,7 @@ }, { "name": "godot_string_operator_index", - "return_type": "const wchar_t *", + "return_type": "const godot_char_type *", "arguments": [ ["godot_string *", "p_self"], ["const godot_int", "p_idx"] @@ -3765,15 +3862,15 @@ }, { "name": "godot_string_operator_index_const", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ ["const godot_string *", "p_self"], ["const godot_int", "p_idx"] ] }, { - "name": "godot_string_wide_str", - "return_type": "const wchar_t *", + "name": "godot_string_get_data", + "return_type": "const godot_char_type *", "arguments": [ ["const godot_string *", "p_self"] ] @@ -3807,7 +3904,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"], ["godot_int", "p_to"] ] @@ -3817,7 +3914,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"], ["godot_int", "p_to"] ] @@ -3878,7 +3975,7 @@ }, { "name": "godot_string_bigrams", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"] ] @@ -3887,7 +3984,7 @@ "name": "godot_string_chr", "return_type": "godot_string", "arguments": [ - ["wchar_t", "p_character"] + ["godot_char_type", "p_character"] ] }, { @@ -3898,12 +3995,20 @@ ["const godot_string *", "p_string"] ] }, + { + "name": "godot_string_ends_with_char_array", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const char *", "p_char_array"] + ] + }, { "name": "godot_string_find", "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -3911,7 +4016,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -3920,7 +4025,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"] + ["const godot_packed_string_array *", "p_keys"] ] }, { @@ -3928,7 +4033,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"], + ["const godot_packed_string_array *", "p_keys"], ["godot_int", "p_from"] ] }, @@ -3937,7 +4042,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"], + ["const godot_packed_string_array *", "p_keys"], ["godot_int", "p_from"], ["godot_int *", "r_key"] ] @@ -3947,7 +4052,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -3955,7 +4060,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -3984,27 +4089,13 @@ ["godot_int", "p_len"] ] }, - { - "name": "godot_string_hex_to_int", - "return_type": "godot_int", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { - "name": "godot_string_hex_to_int_without_prefix", - "return_type": "godot_int", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, { "name": "godot_string_insert", "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], ["godot_int", "p_at_pos"], - ["godot_string", "p_string"] + ["const godot_string *", "p_string"] ] }, { @@ -4137,8 +4228,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4146,8 +4237,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4155,8 +4246,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4164,7 +4255,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -4172,7 +4263,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -4180,7 +4271,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -4189,7 +4280,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -4237,15 +4328,15 @@ ] }, { - "name": "godot_string_to_float", - "return_type": "double", + "name": "godot_string_to_int", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_to_int", - "return_type": "godot_int", + "name": "godot_string_to_float", + "return_type": "double", "arguments": [ ["const godot_string *", "p_self"] ] @@ -4278,6 +4369,14 @@ ["const char *", "p_what"] ] }, + { + "name": "godot_string_wchar_to_float", + "return_type": "double", + "arguments": [ + ["const wchar_t *", "p_str"], + ["const wchar_t **", "r_end"] + ] + }, { "name": "godot_string_char_to_int", "return_type": "godot_int", @@ -4287,7 +4386,7 @@ }, { "name": "godot_string_wchar_to_int", - "return_type": "int64_t", + "return_type": "godot_int", "arguments": [ ["const wchar_t *", "p_str"] ] @@ -4301,48 +4400,33 @@ ] }, { - "name": "godot_string_char_to_int64_with_len", - "return_type": "int64_t", + "name": "godot_string_wchar_to_int_with_len", + "return_type": "godot_int", "arguments": [ ["const wchar_t *", "p_str"], ["int", "p_len"] ] }, { - "name": "godot_string_hex_to_int64", - "return_type": "int64_t", + "name": "godot_string_hex_to_int", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_hex_to_int64_with_prefix", - "return_type": "int64_t", + "name": "godot_string_hex_to_int_with_prefix", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, - { - "name": "godot_string_to_int64", - "return_type": "int64_t", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { - "name": "godot_string_unicode_char_to_float", - "return_type": "double", - "arguments": [ - ["const wchar_t *", "p_str"], - ["const wchar_t **", "r_end"] - ] - }, { "name": "godot_string_get_slice_count", "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_splitter"] + ["const godot_string *", "p_splitter"] ] }, { @@ -4350,7 +4434,7 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_splitter"], + ["const godot_string *", "p_splitter"], ["godot_int", "p_slice"] ] }, @@ -4359,13 +4443,13 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["wchar_t", "p_splitter"], + ["godot_char_type", "p_splitter"], ["godot_int", "p_slice"] ] }, { "name": "godot_string_split", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4373,23 +4457,59 @@ }, { "name": "godot_string_split_allow_empty", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, + { + "name": "godot_string_split_with_maxsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, + { + "name": "godot_string_rsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_rsplit_allow_empty", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_rsplit_with_maxsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, { "name": "godot_string_split_floats", - "return_type": "godot_array", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, { - "name": "godot_string_split_floats_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_floats_allow_empty", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4397,31 +4517,31 @@ }, { "name": "godot_string_split_floats_mk", - "return_type": "godot_array", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { - "name": "godot_string_split_floats_mk_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_floats_mk_allow_empty", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { "name": "godot_string_split_ints", - "return_type": "godot_array", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, { - "name": "godot_string_split_ints_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_ints_allow_empty", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4429,27 +4549,35 @@ }, { "name": "godot_string_split_ints_mk", - "return_type": "godot_array", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { - "name": "godot_string_split_ints_mk_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_ints_mk_allow_empty", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { "name": "godot_string_split_spaces", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"] ] }, + { + "name": "godot_string_lstrip", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_chars"] + ] + }, { "name": "godot_string_rstrip", "return_type": "godot_string", @@ -4458,16 +4586,6 @@ ["const godot_string *", "p_chars"] ] }, - { - "name": "godot_string_rsplit", - "return_type": "godot_packed_string_array", - "arguments": [ - ["const godot_string *", "p_self"], - ["const godot_string *", "p_divisor"], - ["const godot_bool", "p_allow_empty"], - ["const godot_int", "p_maxsplit"] - ] - }, { "name": "godot_string_trim_prefix", "return_type": "godot_string", @@ -4486,16 +4604,16 @@ }, { "name": "godot_string_char_lowercase", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ - ["wchar_t", "p_char"] + ["godot_char_type", "p_char"] ] }, { "name": "godot_string_char_uppercase", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ - ["wchar_t", "p_char"] + ["godot_char_type", "p_char"] ] }, { @@ -4536,7 +4654,7 @@ }, { "name": "godot_string_ord_at", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ ["const godot_string *", "p_self"], ["godot_int", "p_idx"] @@ -4558,6 +4676,14 @@ ["godot_int", "p_pos"] ] }, + { + "name": "godot_string_repeat", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_count"] + ] + }, { "name": "godot_string_strip_edges", "return_type": "godot_string", @@ -4591,7 +4717,7 @@ ] }, { - "name": "godot_string_ascii_extended", + "name": "godot_string_latin1", "return_type": "godot_char_string", "arguments": [ ["const godot_string *", "p_self"] @@ -4622,17 +4748,26 @@ ] }, { - "name": "godot_string_chars_to_utf8", - "return_type": "godot_string", + "name": "godot_string_utf16", + "return_type": "godot_char16_string", "arguments": [ - ["const char *", "p_utf8"] + ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_chars_to_utf8_with_len", - "return_type": "godot_string", + "name": "godot_string_parse_utf16", + "return_type": "godot_bool", "arguments": [ - ["const char *", "p_utf8"], + ["godot_string *", "p_self"], + ["const char16_t *", "p_utf16"] + ] + }, + { + "name": "godot_string_parse_utf16_with_len", + "return_type": "godot_bool", + "arguments": [ + ["godot_string *", "p_self"], + ["const char16_t *", "p_utf16"], ["godot_int", "p_len"] ] }, @@ -4666,14 +4801,14 @@ ] }, { - "name": "godot_string_hash_utf8_chars", + "name": "godot_string_hash_wide_chars", "return_type": "uint32_t", "arguments": [ ["const wchar_t *", "p_str"] ] }, { - "name": "godot_string_hash_utf8_chars_with_len", + "name": "godot_string_hash_wide_chars_with_len", "return_type": "uint32_t", "arguments": [ ["const wchar_t *", "p_str"], @@ -4694,6 +4829,20 @@ ["const godot_string *", "p_self"] ] }, + { + "name": "godot_string_sha1_buffer", + "return_type": "godot_packed_byte_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_sha1_text", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, { "name": "godot_string_sha256_buffer", "return_type": "godot_packed_byte_array", @@ -4822,14 +4971,6 @@ ["const godot_string *", "p_self"] ] }, - { - "name": "godot_string_word_wrap", - "return_type": "godot_string", - "arguments": [ - ["const godot_string *", "p_self"], - ["godot_int", "p_chars_per_line"] - ] - }, { "name": "godot_string_xml_escape", "return_type": "godot_string", @@ -4865,6 +5006,21 @@ ["const godot_string *", "p_self"] ] }, + { + "name": "godot_string_join", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_packed_string_array *", "p_parts"] + ] + }, + { + "name": "godot_string_is_valid_filename", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, { "name": "godot_string_is_valid_float", "return_type": "godot_bool", diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index d89383dc1b..0582d95f63 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -38,10 +38,11 @@ extern "C" { #include #include -typedef wchar_t godot_char_type; +typedef char32_t godot_char_type; #define GODOT_STRING_SIZE sizeof(void *) #define GODOT_CHAR_STRING_SIZE sizeof(void *) +#define GODOT_CHAR16_STRING_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED #define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED @@ -58,6 +59,13 @@ typedef struct { } godot_char_string; #endif +#ifndef GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED +#define GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED +typedef struct { + uint8_t _dont_touch_that[GODOT_CHAR16_STRING_SIZE]; +} godot_char16_string; +#endif + // reduce extern "C" nesting for VS2013 #ifdef __cplusplus } @@ -75,13 +83,28 @@ godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs); const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs); void GDAPI godot_char_string_destroy(godot_char_string *p_cs); +godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs); +const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs); +void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs); + void GDAPI godot_string_new(godot_string *r_dest); void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src); -void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size); -const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); -wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); -const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self); +void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents); +void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents); +void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents); +void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents); +void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents); + +void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size); +void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size); + +const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); +godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); +const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self); godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b); godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b); @@ -89,7 +112,7 @@ godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const /* Standard size stuff */ -godot_int GDAPI godot_string_length(const godot_string *p_self); +/*+++*/ godot_int GDAPI godot_string_length(const godot_string *p_self); /* Helpers */ @@ -99,24 +122,25 @@ signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, c godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string); godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array); -godot_array GDAPI godot_string_bigrams(const godot_string *p_self); -godot_string GDAPI godot_string_chr(wchar_t p_character); +godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self); +godot_string GDAPI godot_string_chr(godot_char_type p_character); godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string); -godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to); -godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to); -godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys); -godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from); -godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key); -godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); +godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array); +godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to); +godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to); +godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); +godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys); +godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from); +godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key); +godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values); godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder); godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len); godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self); -godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self); -godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string); +godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self); +godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string); godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self); godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string); godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string); @@ -133,13 +157,13 @@ godot_string GDAPI godot_string_num_scientific(double p_num); godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals); godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits); godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits); -godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); +godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); +godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length); godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character); godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string); @@ -151,64 +175,79 @@ godot_int GDAPI godot_string_to_int(const godot_string *p_self); godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self); godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self); godot_string GDAPI godot_string_capitalize(const godot_string *p_self); + double GDAPI godot_string_char_to_float(const char *p_what); +double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end); + godot_int GDAPI godot_string_char_to_int(const char *p_what); -int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str); +godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str); + godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len); -int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len); -int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self); -int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self); -int64_t GDAPI godot_string_to_int64(const godot_string *p_self); -double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end); +godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len); -godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter); -godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice); -godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice); +godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter); +godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice); +godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice); -godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_spaces(const godot_string *p_self); +godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit); -wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char); -wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char); +godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit); + +godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters); + +godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self); + +godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char); +godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char); godot_string GDAPI godot_string_to_lower(const godot_string *p_self); godot_string GDAPI godot_string_to_upper(const godot_string *p_self); godot_string GDAPI godot_string_get_basename(const godot_string *p_self); godot_string GDAPI godot_string_get_extension(const godot_string *p_self); godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos); -wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx); +godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx); godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file); godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos); +godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count); godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right); godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self); void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars); godot_char_string GDAPI godot_string_ascii(const godot_string *p_self); -godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self); +godot_char_string GDAPI godot_string_latin1(const godot_string *p_self); + godot_char_string GDAPI godot_string_utf8(const godot_string *p_self); godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8); godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len); -godot_string GDAPI godot_string_chars_to_utf8(const char *p_utf8); -godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot_int p_len); + +godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self); +godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16); +godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len); uint32_t GDAPI godot_string_hash(const godot_string *p_self); uint64_t GDAPI godot_string_hash64(const godot_string *p_self); + uint32_t GDAPI godot_string_hash_chars(const char *p_cstr); uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len); -uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str); -uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len); +uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str); +uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len); + godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self); godot_string GDAPI godot_string_md5_text(const godot_string *p_self); +godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self); +godot_string GDAPI godot_string_sha1_text(const godot_string *p_self); godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self); godot_string GDAPI godot_string_sha256_text(const godot_string *p_self); @@ -231,14 +270,15 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self); godot_string GDAPI godot_string_http_escape(const godot_string *p_self); godot_string GDAPI godot_string_http_unescape(const godot_string *p_self); godot_string GDAPI godot_string_json_escape(const godot_string *p_self); -godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line); godot_string GDAPI godot_string_xml_escape(const godot_string *p_self); godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self); godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self); godot_string GDAPI godot_string_percent_decode(const godot_string *p_self); godot_string GDAPI godot_string_percent_encode(const godot_string *p_self); +godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts); +godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix); godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self); @@ -249,8 +289,8 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self); godot_string GDAPI godot_string_dedent(const godot_string *p_self); godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix); godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix); +godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars); godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars); -godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit); void GDAPI godot_string_destroy(godot_string *p_self); diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 019fa0d1f8..8dbaec4e75 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -176,10 +176,10 @@ List generate_c_api_classes() { // Register global constants as a fake GlobalConstants singleton class { ClassAPI global_constants_api; - global_constants_api.class_name = L"GlobalConstants"; + global_constants_api.class_name = "GlobalConstants"; global_constants_api.api_type = ClassDB::API_CORE; global_constants_api.is_singleton = true; - global_constants_api.singleton_name = L"GlobalConstants"; + global_constants_api.singleton_name = "GlobalConstants"; global_constants_api.is_instanciable = false; const int constants_count = GlobalConstants::get_global_constant_count(); for (int i = 0; i < constants_count; ++i) { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index e2038954e3..9a3273d201 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -33,15 +33,15 @@ #include "../gdscript_tokenizer.h" #include "editor/editor_settings.h" -static bool _is_char(CharType c) { +static bool _is_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } -static bool _is_hex_symbol(CharType c) { +static bool _is_hex_symbol(char32_t c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } -static bool _is_bin_symbol(CharType c) { +static bool _is_bin_symbol(char32_t c) { return (c == '0' || c == '1'); } @@ -119,7 +119,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) /* search the line */ bool match = true; - const CharType *start_key = color_regions[c].start_key.c_str(); + const char32_t *start_key = color_regions[c].start_key.get_data(); for (int k = 0; k < start_key_length; k++) { if (start_key[k] != str[from + k]) { match = false; @@ -156,7 +156,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) /* search the line */ int region_end_index = -1; int end_key_length = color_regions[in_region].end_key.length(); - const CharType *end_key = color_regions[in_region].end_key.c_str(); + const char32_t *end_key = color_regions[in_region].end_key.get_data(); for (; from < line_length; from++) { if (line_length - from < end_key_length) { break; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index fefbf906f0..31ce63bc6e 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -635,7 +635,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ case TEXT_CHAR: { VALIDATE_ARG_COUNT(1); VALIDATE_ARG_NUM(0); - CharType result[2] = { *p_args[0], 0 }; + char32_t result[2] = { *p_args[0], 0 }; r_ret = String(result); } break; case TEXT_ORD: { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index d890103f15..e8dea8180a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -327,7 +327,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ bool found = false; const String &line = lines[i]; for (int j = 0; j < line.size(); j++) { - if (line[j] == CharType(0xFFFF)) { + if (line[j] == char32_t(0xFFFF)) { found = true; break; } else if (line[j] == '\t') { diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index f5cf1e29f0..ed27604aec 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -222,7 +222,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { void GDScriptTokenizer::set_source_code(const String &p_source_code) { source = p_source_code; if (source.empty()) { - _source = L""; + _source = U""; } else { _source = source.ptr(); } @@ -263,7 +263,7 @@ bool GDScriptTokenizer::is_past_cursor() const { return true; } -CharType GDScriptTokenizer::_advance() { +char32_t GDScriptTokenizer::_advance() { if (unlikely(_is_at_end())) { return '\0'; } @@ -282,15 +282,15 @@ CharType GDScriptTokenizer::_advance() { return _peek(-1); } -void GDScriptTokenizer::push_paren(CharType p_char) { +void GDScriptTokenizer::push_paren(char32_t p_char) { paren_stack.push_back(p_char); } -bool GDScriptTokenizer::pop_paren(CharType p_expected) { +bool GDScriptTokenizer::pop_paren(char32_t p_expected) { if (paren_stack.empty()) { return false; } - CharType actual = paren_stack.back()->get(); + char32_t actual = paren_stack.back()->get(); paren_stack.pop_back(); return actual == p_expected; @@ -302,19 +302,19 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() { return error; } -static bool _is_alphanumeric(CharType c) { +static bool _is_alphanumeric(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_digit(CharType c) { +static bool _is_digit(char32_t c) { return (c >= '0' && c <= '9'); } -static bool _is_hex_digit(CharType c) { +static bool _is_hex_digit(char32_t c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -static bool _is_binary_digit(CharType c) { +static bool _is_binary_digit(char32_t c) { return (c == '0' || c == '1'); } @@ -404,7 +404,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) { error_stack.push_back(p_error); } -GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) { +GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { if (paren_stack.empty()) { return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren)); } @@ -413,8 +413,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) { return error; } -GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(CharType p_test, Token::Type p_double_type) { - const CharType *next = _current + 1; +GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, Token::Type p_double_type) { + const char32_t *next = _current + 1; int chars = 2; // Two already matched. // Test before consuming characters, since we don't want to consume more than needed. @@ -602,7 +602,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { bool has_decimal = false; bool has_exponent = false; bool has_error = false; - bool (*digit_check_func)(CharType) = _is_digit; + bool (*digit_check_func)(char32_t) = _is_digit; if (_peek(-1) == '.') { has_decimal = true; @@ -762,7 +762,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { _advance(); } - CharType quote_char = _peek(-1); + char32_t quote_char = _peek(-1); if (_peek() == quote_char && _peek(1) == quote_char) { is_multiline = true; @@ -779,7 +779,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { return make_error("Unterminated string."); } - CharType ch = _peek(); + char32_t ch = _peek(); if (ch == '\\') { // Escape pattern. @@ -789,13 +789,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { } // Grab escape character. - CharType code = _peek(); + char32_t code = _peek(); _advance(); if (_is_at_end()) { return make_error("Unterminated string."); } - CharType escaped = 0; + char32_t escaped = 0; bool valid_escape = true; switch (code) { @@ -836,8 +836,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { return make_error("Unterminated string."); } - CharType digit = _peek(); - CharType value = 0; + char32_t digit = _peek(); + char32_t value = 0; if (digit >= '0' && digit <= '9') { value = digit - '0'; } else if (digit >= 'a' && digit <= 'f') { @@ -940,7 +940,7 @@ void GDScriptTokenizer::check_indent() { } for (;;) { - CharType current_indent_char = _peek(); + char32_t current_indent_char = _peek(); int indent_count = 0; if (current_indent_char != ' ' && current_indent_char != '\t' && current_indent_char != '\r' && current_indent_char != '\n' && current_indent_char != '#') { @@ -970,7 +970,7 @@ void GDScriptTokenizer::check_indent() { // Check indent level. bool mixed = false; while (!_is_at_end()) { - CharType space = _peek(); + char32_t space = _peek(); if (space == '\t') { // Consider individual tab columns. column += tab_size - 1; @@ -1103,7 +1103,7 @@ void GDScriptTokenizer::_skip_whitespace() { } for (;;) { - CharType c = _peek(); + char32_t c = _peek(); switch (c) { case ' ': _advance(); @@ -1192,7 +1192,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { return make_token(Token::TK_EOF); } - const CharType c = _advance(); + const char32_t c = _advance(); if (c == '\\') { // Line continuation with backslash. diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 100ed3f132..4453982d08 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -183,14 +183,14 @@ public: private: String source; - const CharType *_source = nullptr; - const CharType *_current = nullptr; + const char32_t *_source = nullptr; + const char32_t *_current = nullptr; int line = -1, column = -1; int cursor_line = -1, cursor_column = -1; int tab_size = 4; // Keep track of multichar tokens. - const CharType *_start = nullptr; + const char32_t *_start = nullptr; int start_line = 0, start_column = 0; int leftmost_column = 0, rightmost_column = 0; @@ -202,30 +202,30 @@ private: Token last_newline; int pending_indents = 0; List indent_stack; - List paren_stack; - CharType indent_char = '\0'; + List paren_stack; + char32_t indent_char = '\0'; int position = 0; int length = 0; _FORCE_INLINE_ bool _is_at_end() { return position >= length; } - _FORCE_INLINE_ CharType _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; } + _FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; } int indent_level() const { return indent_stack.size(); } bool has_error() const { return !error_stack.empty(); } Token pop_error(); - CharType _advance(); + char32_t _advance(); void _skip_whitespace(); void check_indent(); Token make_error(const String &p_message); void push_error(const String &p_message); void push_error(const Token &p_error); - Token make_paren_error(CharType p_paren); + Token make_paren_error(char32_t p_paren); Token make_token(Token::Type p_type); Token make_literal(const Variant &p_literal); Token make_identifier(const StringName &p_identifier); - Token check_vcs_marker(CharType p_test, Token::Type p_double_type); - void push_paren(CharType p_char); - bool pop_paren(CharType p_expected); + Token check_vcs_marker(char32_t p_test, Token::Type p_double_type); + void push_paren(char32_t p_char); + bool pop_paren(char32_t p_expected); void newline(bool p_make_token); Token number(); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 4d79d9d395..668dfd4835 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int start_pos = p_position.character; for (int c = p_position.character; c >= 0; c--) { start_pos = c; - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -500,7 +500,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int end_pos = p_position.character; for (int c = p_position.character; c < line.length(); c++) { - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -552,7 +552,7 @@ Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_positi } while (c >= 0) { - const CharType &character = line[c]; + const char32_t &character = line[c]; if (character == ')') { ++bracket_stack; } else if (character == '(') { diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 430c82953e..f7d6e7e302 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -151,7 +151,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { case '"': { bool verbatim = idx != 0 && code[idx - 1] == '@'; - CharType begin_str = code[idx]; + char32_t begin_str = code[idx]; idx++; String tk_string = String(); while (true) { @@ -170,13 +170,13 @@ ScriptClassParser::Token ScriptClassParser::get_token() { } else if (code[idx] == '\\' && !verbatim) { //escaped characters... idx++; - CharType next = code[idx]; + char32_t next = code[idx]; if (next == 0) { error_str = "Unterminated String"; error = true; return TK_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -234,7 +234,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { //a number - const CharType *rptr; + const char32_t *rptr; double number = String::to_float(&code[idx], &rptr); idx += (rptr - &code[idx]); value = number; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 6d7d5f76cd..c460e283ea 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -311,44 +311,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ return false; } -String mono_to_utf8_string(MonoString *p_mono_string) { - MonoError error; - char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); - - if (!mono_error_ok(&error)) { - ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&error) + "'."); - mono_error_cleanup(&error); - return String(); - } - - String ret = String::utf8(utf8); - - mono_free(utf8); - - return ret; -} - -String mono_to_utf16_string(MonoString *p_mono_string) { - int len = mono_string_length(p_mono_string); - String ret; - - if (len == 0) { - return ret; - } - - ret.resize(len + 1); - ret.set(len, 0); - - CharType *src = (CharType *)mono_string_chars(p_mono_string); - CharType *dst = ret.ptrw(); - - for (int i = 0; i < len; i++) { - dst[i] = src[i]; - } - - return ret; -} - MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 4ff330fd43..a1fd975916 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -69,15 +69,11 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ // String -String mono_to_utf8_string(MonoString *p_mono_string); -String mono_to_utf16_string(MonoString *p_mono_string); - _FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_to_utf16_string(p_mono_string); - } - - return mono_to_utf8_string(p_mono_string); + char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string); + String ret = String(utf32); + mono_free(utf32); + return ret; } _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { @@ -88,20 +84,8 @@ _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { return mono_string_to_godot_not_null(p_mono_string); } -_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) { - return mono_string_new(mono_domain_get(), p_string.utf8().get_data()); -} - -_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) { - return mono_string_from_utf16((mono_unichar2 *)p_string.c_str()); -} - _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_from_utf16_string(p_string); - } - - return mono_from_utf8_string(p_string); + return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data())); } // Variant diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index e0cf916a01..a619f0b975 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -71,12 +71,12 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) buffer.resize(512); DWORD dwBufferSize = buffer.size(); - LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); if (res == ERROR_MORE_DATA) { // dwBufferSize now contains the actual size buffer.resize(dwBufferSize); - res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); } if (res == ERROR_SUCCESS) { @@ -90,7 +90,7 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) { HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; @@ -127,7 +127,7 @@ LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) { String default_clr; HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ccfaf5aba7..d91dcb5981 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -86,7 +86,7 @@ String abspath(const String &p_path) { String realpath(const String &p_path) { #ifdef WINDOWS_ENABLED // Open file without read/write access - HANDLE hFile = ::CreateFileW(p_path.c_str(), 0, + HANDLE hFile = ::CreateFileW((LPCWSTR)(p_path.utf16().get_data()), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -130,7 +130,7 @@ String join(const String &p_a, const String &p_b) { return p_b; } - const CharType a_last = p_a[p_a.length() - 1]; + const char32_t a_last = p_a[p_a.length() - 1]; if ((a_last == '/' || a_last == '\\') || (p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) { return p_a + p_b; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index f8d9804de4..65da4328f6 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -49,7 +49,7 @@ int sfind(const String &p_text, int p_from) { return -1; } - const CharType *src = p_text.c_str(); + const char32_t *src = p_text.get_data(); for (int i = p_from; i <= (len - src_len); i++) { bool found = true; @@ -64,7 +64,7 @@ int sfind(const String &p_text, int p_from) { found = src[read_pos] == '%'; break; case 1: { - CharType c = src[read_pos]; + char32_t c = src[read_pos]; found = src[read_pos] == 's' || (c >= '0' && c <= '4'); break; } @@ -121,7 +121,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const int result = 0; while ((result = sfind(p_text, search_from)) >= 0) { - CharType c = p_text[result + 1]; + char32_t c = p_text[result + 1]; int req_index = (c == 's' ? findex++ : c - '0'); diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 50ca01067b..c10a276eae 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -156,26 +156,13 @@ void RegExMatch::_bind_methods() { } void RegEx::_pattern_info(uint32_t what, void *where) const { - if (sizeof(CharType) == 2) { - pcre2_pattern_info_16((pcre2_code_16 *)code, what, where); - - } else { - pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); - } + pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); } void RegEx::clear() { - if (sizeof(CharType) == 2) { - if (code) { - pcre2_code_free_16((pcre2_code_16 *)code); - code = nullptr; - } - - } else { - if (code) { - pcre2_code_free_32((pcre2_code_32 *)code); - code = nullptr; - } + if (code) { + pcre2_code_free_32((pcre2_code_32 *)code); + code = nullptr; } } @@ -187,39 +174,20 @@ Error RegEx::compile(const String &p_pattern) { PCRE2_SIZE offset; uint32_t flags = PCRE2_DUPNAMES; - if (sizeof(CharType) == 2) { - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx); - PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str(); + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); + PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.get_data(); - code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx); + code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - pcre2_compile_context_free_16(cctx); + pcre2_compile_context_free_32(cctx); - if (!code) { - PCRE2_UCHAR16 buf[256]; - pcre2_get_error_message_16(err, buf, 256); - String message = String::num(offset) + ": " + String((const CharType *)buf); - ERR_PRINT(message.utf8()); - return FAILED; - } - - } else { - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); - PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str(); - - code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - - pcre2_compile_context_free_32(cctx); - - if (!code) { - PCRE2_UCHAR32 buf[256]; - pcre2_get_error_message_32(err, buf, 256); - String message = String::num(offset) + ": " + String((const CharType *)buf); - ERR_PRINT(message.utf8()); - return FAILED; - } + if (!code) { + PCRE2_UCHAR32 buf[256]; + pcre2_get_error_message_32(err, buf, 256); + String message = String::num(offset) + ": " + String((const char32_t *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } return OK; } @@ -234,69 +202,39 @@ Ref RegEx::search(const String &p_subject, int p_offset, int p_end) length = p_end; } - if (sizeof(CharType) == 2) { - pcre2_code_16 *c = (pcre2_code_16 *)code; - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); - PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data(); - pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx); - - if (res < 0) { - pcre2_match_data_free_16(match); - return nullptr; - } - - uint32_t size = pcre2_get_ovector_count_16(match); - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match); - - result->data.resize(size); - - for (uint32_t i = 0; i < size; i++) { - result->data.write[i].start = ovector[i * 2]; - result->data.write[i].end = ovector[i * 2 + 1]; - } - - pcre2_match_data_free_16(match); - pcre2_match_context_free_16(mctx); - - } else { - pcre2_code_32 *c = (pcre2_code_32 *)code; - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); - PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); - - pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - - int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); - - if (res < 0) { - pcre2_match_data_free_32(match); - pcre2_match_context_free_32(mctx); - - return nullptr; - } - - uint32_t size = pcre2_get_ovector_count_32(match); - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); - - result->data.resize(size); - - for (uint32_t i = 0; i < size; i++) { - result->data.write[i].start = ovector[i * 2]; - result->data.write[i].end = ovector[i * 2 + 1]; - } + int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); + if (res < 0) { pcre2_match_data_free_32(match); pcre2_match_context_free_32(mctx); + + return nullptr; } + uint32_t size = pcre2_get_ovector_count_32(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); + + result->data.resize(size); + + for (uint32_t i = 0; i < size; i++) { + result->data.write[i].start = ovector[i * 2]; + result->data.write[i].end = ovector[i * 2 + 1]; + } + + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); + result->subject = p_subject; uint32_t count; - const CharType *table; + const char32_t *table; uint32_t entry_size; _pattern_info(PCRE2_INFO_NAMECOUNT, &count); @@ -304,7 +242,7 @@ Ref RegEx::search(const String &p_subject, int p_offset, int p_end) _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); for (uint32_t i = 0; i < count; i++) { - CharType id = table[i * entry_size]; + char32_t id = table[i * entry_size]; if (result->data[id].start == -1) { continue; } @@ -344,7 +282,7 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a const int safety_zone = 1; PCRE2_SIZE olength = p_subject.length() + 1; // space for output string and one terminating \0 character - Vector output; + Vector output; output.resize(olength + safety_zone); uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH; @@ -357,55 +295,28 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a length = p_end; } - if (sizeof(CharType) == 2) { - pcre2_code_16 *c = (pcre2_code_16 *)code; - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); - PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); - PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str(); - PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.ptrw(); + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data(); + PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.get_data(); + PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw(); - pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); + int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - if (res == PCRE2_ERROR_NOMEMORY) { - output.resize(olength + safety_zone); - o = (PCRE2_UCHAR16 *)output.ptrw(); - res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - } + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength + safety_zone); + o = (PCRE2_UCHAR32 *)output.ptrw(); + res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); + } - pcre2_match_data_free_16(match); - pcre2_match_context_free_16(mctx); + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); - if (res < 0) { - return String(); - } - - } else { - pcre2_code_32 *c = (pcre2_code_32 *)code; - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); - PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); - PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str(); - PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw(); - - pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - - int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - - if (res == PCRE2_ERROR_NOMEMORY) { - output.resize(olength + safety_zone); - o = (PCRE2_UCHAR32 *)output.ptrw(); - res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - } - - pcre2_match_data_free_32(match); - pcre2_match_context_free_32(mctx); - - if (res < 0) { - return String(); - } + if (res < 0) { + return String(); } return String(output.ptr(), olength); @@ -435,7 +346,7 @@ Array RegEx::get_names() const { ERR_FAIL_COND_V(!is_valid(), result); uint32_t count; - const CharType *table; + const char32_t *table; uint32_t entry_size; _pattern_info(PCRE2_INFO_NAMECOUNT, &count); @@ -453,39 +364,21 @@ Array RegEx::get_names() const { } RegEx::RegEx() { - if (sizeof(CharType) == 2) { - general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr); - - } else { - general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); - } + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); code = nullptr; } RegEx::RegEx(const String &p_pattern) { - if (sizeof(CharType) == 2) { - general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr); - - } else { - general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); - } + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); code = nullptr; compile(p_pattern); } RegEx::~RegEx() { - if (sizeof(CharType) == 2) { - if (code) { - pcre2_code_free_16((pcre2_code_16 *)code); - } - pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx); - - } else { - if (code) { - pcre2_code_free_32((pcre2_code_32 *)code); - } - pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); + if (code) { + pcre2_code_free_32((pcre2_code_32 *)code); } + pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); } void RegEx::_bind_methods() { diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index a0dcd76d10..177f9192b8 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -1060,7 +1060,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in } break; case VisualScriptBuiltinFunc::TEXT_CHAR: { - CharType result[2] = { *p_inputs[0], 0 }; + char32_t result[2] = { *p_inputs[0], 0 }; *r_return = String(result); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 2ac7793b8c..60a439b291 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -187,7 +187,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) - CharType cchar = GET_CHAR(); + char32_t cchar = GET_CHAR(); if (cchar == 0) { r_token.type = TK_EOF; return OK; @@ -329,7 +329,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { case '"': { String str; while (true) { - CharType ch = GET_CHAR(); + char32_t ch = GET_CHAR(); if (ch == 0) { _set_error("Unterminated String"); @@ -340,13 +340,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) { } else if (ch == '\\') { //escaped characters... - CharType next = GET_CHAR(); + char32_t next = GET_CHAR(); if (next == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -367,7 +367,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = GET_CHAR(); + char32_t c = GET_CHAR(); if (c == 0) { _set_error("Unterminated String"); @@ -379,7 +379,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -431,7 +431,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 87aa64211e..1b77ed3168 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -933,36 +933,36 @@ static const char *op_names[] = { }; String VisualScriptOperator::get_caption() const { - static const wchar_t *op_names[] = { + static const char32_t *op_names[] = { //comparison - L"A = B", //OP_EQUAL, - L"A \u2260 B", //OP_NOT_EQUAL, - L"A < B", //OP_LESS, - L"A \u2264 B", //OP_LESS_EQUAL, - L"A > B", //OP_GREATER, - L"A \u2265 B", //OP_GREATER_EQUAL, + U"A = B", //OP_EQUAL, + U"A \u2260 B", //OP_NOT_EQUAL, + U"A < B", //OP_LESS, + U"A \u2264 B", //OP_LESS_EQUAL, + U"A > B", //OP_GREATER, + U"A \u2265 B", //OP_GREATER_EQUAL, //mathematic - L"A + B", //OP_ADD, - L"A - B", //OP_SUBTRACT, - L"A \u00D7 B", //OP_MULTIPLY, - L"A \u00F7 B", //OP_DIVIDE, - L"\u00AC A", //OP_NEGATE, - L"+ A", //OP_POSITIVE, - L"A mod B", //OP_MODULE, - L"A .. B", //OP_STRING_CONCAT, + U"A + B", //OP_ADD, + U"A - B", //OP_SUBTRACT, + U"A \u00D7 B", //OP_MULTIPLY, + U"A \u00F7 B", //OP_DIVIDE, + U"\u00AC A", //OP_NEGATE, + U"+ A", //OP_POSITIVE, + U"A mod B", //OP_MODULE, + U"A .. B", //OP_STRING_CONCAT, //bitwise - L"A << B", //OP_SHIFT_LEFT, - L"A >> B", //OP_SHIFT_RIGHT, - L"A & B", //OP_BIT_AND, - L"A | B", //OP_BIT_OR, - L"A ^ B", //OP_BIT_XOR, - L"~A", //OP_BIT_NEGATE, + U"A << B", //OP_SHIFT_LEFT, + U"A >> B", //OP_SHIFT_RIGHT, + U"A & B", //OP_BIT_AND, + U"A | B", //OP_BIT_OR, + U"A ^ B", //OP_BIT_XOR, + U"~A", //OP_BIT_NEGATE, //logic - L"A and B", //OP_AND, - L"A or B", //OP_OR, - L"A xor B", //OP_XOR, - L"not A", //OP_NOT, - L"A in B", //OP_IN, + U"A and B", //OP_AND, + U"A or B", //OP_OR, + U"A xor B", //OP_XOR, + U"not A", //OP_NOT, + U"A in B", //OP_IN, }; return op_names[op]; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 95b778caf6..e318b35495 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -453,7 +453,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String name; bool first = true; for (int i = 0; i < basename.length(); i++) { - CharType c = basename[i]; + char32_t c = basename[i]; if (c >= '0' && c <= '9' && first) { continue; } @@ -484,7 +484,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { int segments = 0; bool first = true; for (int i = 0; i < pname.length(); i++) { - CharType c = pname[i]; + char32_t c = pname[i]; if (first && c == '.') { if (r_error) { *r_error = TTR("Package segments must be of non-zero length."); @@ -873,7 +873,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (string_flags & UTF8_FLAG) { } else { uint32_t len = decode_uint16(&p_manifest[string_at]); - Vector ucstring; + Vector ucstring; ucstring.resize(len + 1); for (uint32_t j = 0; j < len; j++) { uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); @@ -1334,7 +1334,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } else { String str; for (uint32_t i = 0; i < len; i++) { - CharType c = decode_uint16(&p_bytes[offset + i * 2]); + char32_t c = decode_uint16(&p_bytes[offset + i * 2]); if (c == 0) { break; } diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm index 1721da3db6..eea87cecc9 100644 --- a/platform/iphone/display_server_iphone.mm +++ b/platform/iphone/display_server_iphone.mm @@ -701,7 +701,7 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri if (p_path.begins_with("res://")) { if (PackedData::get_singleton()->has_path(p_path)) { - printf("Unable to play %S using the native player as it resides in a .pck file\n", p_path.c_str()); + printf("Unable to play %s using the native player as it resides in a .pck file\n", p_path.utf8().get_data()); return ERR_INVALID_PARAMETER; } else { p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path()); @@ -712,7 +712,7 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri memdelete(f); - printf("Playing video: %S\n", p_path.c_str()); + printf("Playing video: %s\n", p_path.utf8().get_data()); String file_path = ProjectSettings::get_singleton()->globalize_path(p_path); diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index a889717f20..97f954ebb2 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -115,7 +115,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { } for (int i = 0; i < pname.length(); i++) { - CharType c = pname[i]; + char32_t c = pname[i]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { if (r_error) { *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index dfec5d7634..1477f92200 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -138,7 +138,7 @@ Error InAppStore::request_product_info(Dictionary p_params) { NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease]; for (int i = 0; i < pids.size(); i++) { - printf("******** adding %ls to product list\n", pids[i].c_str()); + printf("******** adding %s to product list\n", pids[i].utf8().get_data()); NSString *pid = [[[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()] autorelease]; [array addObject:pid]; }; diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index ad26d0ada3..6d7699c0c9 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -86,7 +86,7 @@ String iOS::get_rate_url(int p_app_id) const { // ios7 for everything? ret = templ_iOS7.replace("APP_ID", String::num(p_app_id)); - printf("returning rate url %ls\n", ret.c_str()); + printf("returning rate url %s\n", ret.utf8().get_data()); return ret; }; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index d1a69642b1..946fd51923 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -278,7 +278,7 @@ Error OSIPhone::shell_open(String p_uri) { return ERR_CANT_OPEN; } - printf("opening url %ls\n", p_uri.c_str()); + printf("opening url %s\n", p_uri.utf8().get_data()); // if (@available(iOS 10, *)) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; @@ -293,7 +293,7 @@ void OSIPhone::set_user_data_dir(String p_dir) { DirAccess *da = DirAccess::open(p_dir); user_data_dir = da->get_current_dir(); - printf("setting data dir to %ls from %ls\n", user_data_dir.c_str(), p_dir.c_str()); + printf("setting data dir to %s from %s\n", user_data_dir.utf8().get_data(), p_dir.utf8().get_data()); memdelete(da); } diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp index b3553e961a..e2b88b7704 100644 --- a/platform/linuxbsd/crash_handler_linuxbsd.cpp +++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp @@ -67,7 +67,7 @@ static void handle_crash(int sig) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); } - fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { for (size_t i = 1; i < size; i++) { @@ -109,7 +109,7 @@ static void handle_crash(int sig) { output.erase(output.length() - 1, 1); } - fprintf(stderr, "[%ld] %s (%ls)\n", (long int)i, fname, output.c_str()); + fprintf(stderr, "[%ld] %s (%s)\n", (long int)i, fname, output.utf8().get_data()); } free(strings); diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 5da0118686..9fb2f63935 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -90,7 +90,7 @@ static void handle_crash(int sig) { if (OS::get_singleton()->get_main_loop()) OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); - fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { void *load_addr = (void *)load_address(); @@ -142,7 +142,7 @@ static void handle_crash(int sig) { } } - fprintf(stderr, "[%zu] %ls\n", i, output.c_str()); + fprintf(stderr, "[%zu] %s\n", i, output.utf8().get_data()); } free(strings); diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 0cf02ef69b..9f2160dd9e 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -78,7 +78,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform { } for (int i = 0; i < pname.length(); i++) { - CharType c = pname[i]; + char32_t c = pname[i]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { if (r_error) { *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 401ba6c35d..44ab075816 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -296,7 +296,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a void OS_UWP::set_clipboard(const String &p_text) { DataPackage ^ clip = ref new DataPackage(); clip->RequestedOperation = DataPackageOperation::Copy; - clip->SetText(ref new Platform::String((const wchar_t *)p_text.c_str())); + clip->SetText(ref new Platform::String((LPCWSTR)(p_text.utf16().get_data()))); Clipboard::SetContent(clip); }; @@ -346,8 +346,8 @@ void OS_UWP::finalize_core() { } void OS_UWP::alert(const String &p_alert, const String &p_title) { - Platform::String ^ alert = ref new Platform::String(p_alert.c_str()); - Platform::String ^ title = ref new Platform::String(p_title.c_str()); + Platform::String ^ alert = ref new Platform::String((LPCWSTR)(p_alert.utf16().get_data())); + Platform::String ^ title = ref new Platform::String((LPCWSTR)(p_title.utf16().get_data())); MessageDialog ^ msg = ref new MessageDialog(alert, title); @@ -738,7 +738,7 @@ static String format_error_message(DWORD id) { Error OS_UWP::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { String full_path = "game/" + p_path; - p_library_handle = (void *)LoadPackagedLibrary(full_path.c_str(), 0); + p_library_handle = (void *)LoadPackagedLibrary((LPCWSTR)(full_path.utf16().get_data()), 0); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + full_path + ", error: " + format_error_message(GetLastError()) + "."); return OK; } diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 996d9722f5..02031ef6bb 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -175,7 +175,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { msg = proj_settings->get("debug/settings/crash_handler/message"); } - fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); int n = 0; do { diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 11f58d1be0..7f4669b3b2 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -42,7 +42,7 @@ static String format_error_message(DWORD id) { size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); - String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size); LocalFree(messageBuffer); @@ -78,7 +78,7 @@ String DisplayServerWindows::get_name() const { } void DisplayServerWindows::alert(const String &p_alert, const String &p_title) { - MessageBoxW(nullptr, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL); + MessageBoxW(nullptr, (LPCWSTR)(p_alert.utf16().get_data()), (LPCWSTR)(p_title.utf16().get_data()), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL); } void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { @@ -177,11 +177,12 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { } EmptyClipboard(); - HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType)); + Char16String utf16 = text.utf16(); + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR)); ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents."); LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); - memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType)); + memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR)); GlobalUnlock(mem); SetClipboardData(CF_UNICODETEXT, mem); @@ -218,7 +219,7 @@ String DisplayServerWindows::clipboard_get() const { if (mem != nullptr) { LPWSTR ptr = (LPWSTR)GlobalLock(mem); if (ptr != nullptr) { - ret = String((CharType *)ptr); + ret = String::utf16((const char16_t *)ptr); GlobalUnlock(mem); }; }; @@ -593,7 +594,7 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); - SetWindowTextW(windows[p_window].hWnd, p_title.c_str()); + SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data())); } int DisplayServerWindows::window_get_current_screen(WindowID p_window) const { @@ -1423,13 +1424,13 @@ String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); GetKeyboardLayoutList(layout_count, layouts); - wchar_t buf[LOCALE_NAME_MAX_LENGTH]; - memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + WCHAR buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); memfree(layouts); - return String(buf).substr(0, 2); + return String::utf16((const char16_t *)buf).substr(0, 2); } String _get_full_layout_name_from_registry(HKL p_layout) { @@ -1437,17 +1438,17 @@ String _get_full_layout_name_from_registry(HKL p_layout) { String ret; HKEY hkey; - wchar_t layout_text[1024]; - memset(layout_text, 0, 1024 * sizeof(wchar_t)); + WCHAR layout_text[1024]; + memset(layout_text, 0, 1024 * sizeof(WCHAR)); - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)id.c_str(), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { return ret; } DWORD buffer = 1024; DWORD vtype = REG_SZ; if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { - ret = String(layout_text); + ret = String::utf16((const char16_t *)layout_text); } RegCloseKey(hkey); return ret; @@ -1463,15 +1464,15 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine). if (ret == String()) { - wchar_t buf[LOCALE_NAME_MAX_LENGTH]; - memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + WCHAR buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); - wchar_t name[1024]; - memset(name, 0, 1024 * sizeof(wchar_t)); + WCHAR name[1024]; + memset(name, 0, 1024 * sizeof(WCHAR)); GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024); - ret = String(name); + ret = String::utf16((const char16_t *)name); } memfree(layouts); @@ -2711,7 +2712,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_DROPFILES: { HDROP hDropInfo = (HDROP)wParam; const int buffsize = 4096; - wchar_t buf[buffsize]; + WCHAR buf[buffsize]; int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0); @@ -2719,7 +2720,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA for (int i = 0; i < fcount; i++) { DragQueryFileW(hDropInfo, i, buf, buffsize); - String file = buf; + String file = String::utf16((const char16_t *)buf); files.push_back(file); } diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 5b15896b0c..f73516b370 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -84,7 +84,7 @@ static String format_error_message(DWORD id) { size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); - String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size); LocalFree(messageBuffer); @@ -107,15 +107,11 @@ void RedirectIOToConsole() { // set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), - - &coninfo); + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), - - coninfo.dwSize); + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); // redirect unbuffered STDOUT to the console @@ -265,10 +261,10 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han DLL_DIRECTORY_COOKIE cookie = nullptr; if (p_also_set_library_path && has_dll_directory_api) { - cookie = add_dll_directory(path.get_base_dir().c_str()); + cookie = add_dll_directory((LPCWSTR)(path.get_base_dir().utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW(path.c_str(), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + format_error_message(GetLastError()) + "."); if (cookie) { @@ -407,7 +403,7 @@ uint64_t OS_Windows::get_ticks_usec() const { String OS_Windows::_quote_command_line_argument(const String &p_text) const { for (int i = 0; i < p_text.size(); i++) { - CharType c = p_text[i]; + char32_t c = p_text[i]; if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') { return "\"" + p_text + "\""; } @@ -428,7 +424,7 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, // Note: _wpopen is calling command as "cmd.exe /c argss", instead of executing it directly, add extra quotes around full command, to prevent it from stripping quotes in the command. argss = _quote_command_line_argument(argss); - FILE *f = _wpopen(argss.c_str(), L"r"); + FILE *f = _wpopen((LPCWSTR)(argss.utf16().get_data()), L"r"); ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); char buf[65535]; @@ -463,13 +459,8 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, ZeroMemory(&pi.pi, sizeof(pi.pi)); LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; - Vector modstr; // Windows wants to change this no idea why. - modstr.resize(cmdline.size()); - for (int i = 0; i < cmdline.size(); i++) { - modstr.write[i] = cmdline[i]; - } - - int ret = CreateProcessW(nullptr, modstr.ptrw(), nullptr, nullptr, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); + Char16String modstr = cmdline.utf16(); // Windows wants to change this no idea why. + int ret = CreateProcessW(nullptr, (LPWSTR)(modstr.ptrw()), nullptr, nullptr, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); if (p_blocking) { @@ -509,26 +500,26 @@ int OS_Windows::get_process_id() const { } Error OS_Windows::set_cwd(const String &p_cwd) { - if (_wchdir(p_cwd.c_str()) != 0) + if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) return ERR_CANT_OPEN; return OK; } String OS_Windows::get_executable_path() const { - wchar_t bufname[4096]; + WCHAR bufname[4096]; GetModuleFileNameW(nullptr, bufname, 4096); - String s = bufname; + String s = String::utf16((const char16_t *)bufname); return s; } bool OS_Windows::has_environment(const String &p_var) const { #ifdef MINGW_ENABLED - return _wgetenv(p_var.c_str()) != nullptr; + return _wgetenv((LPCWSTR)(p_var.utf16().get_data())) != nullptr; #else - wchar_t *env; + WCHAR *env; size_t len; - _wdupenv_s(&env, &len, p_var.c_str()); + _wdupenv_s(&env, &len, (LPCWSTR)(p_var.utf16().get_data())); const bool has_env = env != nullptr; free(env); return has_env; @@ -536,16 +527,16 @@ bool OS_Windows::has_environment(const String &p_var) const { }; String OS_Windows::get_environment(const String &p_var) const { - wchar_t wval[0x7Fff]; // MSDN says 32767 char is the maximum - int wlen = GetEnvironmentVariableW(p_var.c_str(), wval, 0x7Fff); + WCHAR wval[0x7fff]; // MSDN says 32767 char is the maximum + int wlen = GetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), wval, 0x7fff); if (wlen > 0) { - return wval; + return String::utf16((const char16_t *)wval); } return ""; } bool OS_Windows::set_environment(const String &p_var, const String &p_value) const { - return (bool)SetEnvironmentVariableW(p_var.c_str(), p_value.c_str()); + return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data())); } String OS_Windows::get_stdin_string(bool p_block) { @@ -558,7 +549,7 @@ String OS_Windows::get_stdin_string(bool p_block) { } Error OS_Windows::shell_open(String p_uri) { - ShellExecuteW(nullptr, nullptr, p_uri.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ShellExecuteW(nullptr, nullptr, (LPCWSTR)(p_uri.utf16().get_data()), nullptr, nullptr, SW_SHOWNORMAL); return OK; } @@ -701,7 +692,7 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const { PWSTR szPath; HRESULT res = SHGetKnownFolderPath(id, 0, nullptr, &szPath); ERR_FAIL_COND_V(res != S_OK, String()); - String path = String(szPath); + String path = String::utf16((const char16_t *)szPath); CoTaskMemFree(szPath); return path; } @@ -727,7 +718,7 @@ String OS_Windows::get_user_data_dir() const { String OS_Windows::get_unique_id() const { HW_PROFILE_INFO HwProfInfo; ERR_FAIL_COND_V(!GetCurrentHwProfile(&HwProfInfo), ""); - return String(HwProfInfo.szHwProfileGuid); + return String::utf16((const char16_t *)(HwProfInfo.szHwProfileGuid), HW_PROFILE_GUIDLEN); } bool OS_Windows::_check_internal_feature_support(const String &p_feature) { @@ -744,9 +735,11 @@ bool OS_Windows::is_disable_crash_handler() const { Error OS_Windows::move_to_trash(const String &p_path) { SHFILEOPSTRUCTW sf; - WCHAR *from = new WCHAR[p_path.length() + 2]; - wcscpy_s(from, p_path.length() + 1, p_path.c_str()); - from[p_path.length() + 1] = 0; + + Char16String utf16 = p_path.utf16(); + WCHAR *from = new WCHAR[utf16.length() + 2]; + wcscpy_s(from, utf16.length() + 1, (LPCWSTR)(utf16.get_data())); + from[utf16.length() + 1] = 0; sf.hwnd = main_window; sf.wFunc = FO_DELETE; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index f49acc1b96..9f3db2ba20 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -235,8 +235,8 @@ void Label::_notification(int p_what) { float x_ofs_shadow = x_ofs; for (int i = 0; i < from->word_len; i++) { if (visible_chars < 0 || chars_total_shadow < visible_chars) { - CharType c = xl_text[i + pos]; - CharType n = xl_text[i + pos + 1]; + char32_t c = xl_text[i + pos]; + char32_t n = xl_text[i + pos + 1]; if (uppercase) { c = String::char_uppercase(c); n = String::char_uppercase(n); @@ -255,8 +255,8 @@ void Label::_notification(int p_what) { } for (int i = 0; i < from->word_len; i++) { if (visible_chars < 0 || chars_total < visible_chars) { - CharType c = xl_text[i + pos]; - CharType n = xl_text[i + pos + 1]; + char32_t c = xl_text[i + pos]; + char32_t n = xl_text[i + pos + 1]; if (uppercase) { c = String::char_uppercase(c); n = String::char_uppercase(n); @@ -308,7 +308,7 @@ int Label::get_longest_line_width() const { real_t line_width = 0; for (int i = 0; i < xl_text.size(); i++) { - CharType current = xl_text[i]; + char32_t current = xl_text[i]; if (uppercase) { current = String::char_uppercase(current); } @@ -390,7 +390,7 @@ void Label::regenerate_word_cache() { WordCache *last = nullptr; for (int i = 0; i <= xl_text.length(); i++) { - CharType current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works + char32_t current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works if (uppercase) { current = String::char_uppercase(current); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 14167531a5..8b95acff70 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -42,7 +42,7 @@ #include "editor/editor_settings.h" #endif #include "scene/main/window.h" -static bool _is_text_char(CharType c) { +static bool _is_text_char(char32_t c) { return !is_symbol(c); } @@ -582,7 +582,7 @@ void LineEdit::_gui_input(Ref p_event) { if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) { if (editable) { selection_delete(); - CharType ucodestr[2] = { (CharType)k->get_unicode(), 0 }; + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; int prev_len = text.length(); append_at_cursor(ucodestr); if (text.length() != prev_len) { @@ -807,8 +807,8 @@ void LineEdit::_notification(int p_what) { break; } - CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; + char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) { @@ -830,8 +830,8 @@ void LineEdit::_notification(int p_what) { } } - CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs]; - CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; + char32_t cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs]; + char32_t next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; int char_width = font->get_char_size(cchar, next).width; // End of widget, break. @@ -870,8 +870,8 @@ void LineEdit::_notification(int p_what) { break; } - CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; + char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) { diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h index 77c82aa780..a5401f7eaa 100644 --- a/scene/gui/rich_text_effect.h +++ b/scene/gui/rich_text_effect.h @@ -59,7 +59,7 @@ public: bool visibility; Point2 offset; Color color; - CharType character; + char32_t character; float elapsed_time; Dictionary environment; @@ -79,7 +79,7 @@ public: Color get_color() { return color; } void set_color(Color p_color) { color = p_color; } int get_character() { return (int)character; } - void set_character(int p_char) { character = (CharType)p_char; } + void set_character(int p_char) { character = (char32_t)p_char; } Dictionary get_environment() { return environment; } void set_environment(Dictionary p_environment) { environment = p_environment; } }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 29337e20f3..c62fd24a5e 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -354,8 +354,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & font = p_base_font; } - const CharType *c = text->text.c_str(); - const CharType *cf = c; + const char32_t *c = text->text.get_data(); + const char32_t *cf = c; int ascent = font->get_ascent(); int descent = font->get_descent(); @@ -461,7 +461,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & bool selected = false; Color fx_color = Color(color); Point2 fx_offset; - CharType fx_char = c[i]; + char32_t fx_char = c[i]; if (selection.active) { int cofs = (&c[i]) - cf; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d6d8e74748..1d732bec5b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -44,23 +44,23 @@ #define TAB_PIXELS -inline bool _is_symbol(CharType c) { +inline bool _is_symbol(char32_t c) { return is_symbol(c); } -static bool _is_text_char(CharType c) { +static bool _is_text_char(char32_t c) { return !is_symbol(c); } -static bool _is_whitespace(CharType c) { +static bool _is_whitespace(char32_t c) { return c == '\t' || c == ' '; } -static bool _is_char(CharType c) { +static bool _is_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } -static bool _is_pair_right_symbol(CharType c) { +static bool _is_pair_right_symbol(char32_t c) { return c == '"' || c == '\'' || c == ')' || @@ -68,7 +68,7 @@ static bool _is_pair_right_symbol(CharType c) { c == '}'; } -static bool _is_pair_left_symbol(CharType c) { +static bool _is_pair_left_symbol(char32_t c) { return c == '"' || c == '\'' || c == '(' || @@ -76,11 +76,11 @@ static bool _is_pair_left_symbol(CharType c) { c == '{'; } -static bool _is_pair_symbol(CharType c) { +static bool _is_pair_symbol(char32_t c) { return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); } -static CharType _get_right_pair_symbol(CharType c) { +static char32_t _get_right_pair_symbol(char32_t c) { if (c == '"') { return '"'; } @@ -119,7 +119,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { int w = 0; int len = text[p_line].data.length(); - const CharType *str = text[p_line].data.c_str(); + const char32_t *str = text[p_line].data.get_data(); // Update width. @@ -214,7 +214,7 @@ void TextEdit::Text::remove(int p_at) { text.remove(p_at); } -int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const { +int TextEdit::Text::get_char_width(char32_t c, char32_t next_c, int px) const { int tab_w = font->get_char_size(' ').width * indent_size; int w = 0; @@ -654,8 +654,8 @@ void TextEdit::_notification(int p_what) { if (brace_matching_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) { if (cursor.column < text[cursor.line].length()) { // Check for open. - CharType c = text[cursor.line][cursor.column]; - CharType closec = 0; + char32_t c = text[cursor.line][cursor.column]; + char32_t closec = 0; if (c == '[') { closec = ']'; @@ -671,10 +671,10 @@ void TextEdit::_notification(int p_what) { for (int i = cursor.line; i < text.size(); i++) { int from = i == cursor.line ? cursor.column + 1 : 0; for (int j = from; j < text[i].length(); j++) { - CharType cc = text[i][j]; + char32_t cc = text[i][j]; // Ignore any brackets inside a string. if (cc == '"' || cc == '\'') { - CharType quotation = cc; + char32_t quotation = cc; do { j++; if (!(j < text[i].length())) { @@ -720,8 +720,8 @@ void TextEdit::_notification(int p_what) { } if (cursor.column > 0) { - CharType c = text[cursor.line][cursor.column - 1]; - CharType closec = 0; + char32_t c = text[cursor.line][cursor.column - 1]; + char32_t closec = 0; if (c == ']') { closec = '['; @@ -737,10 +737,10 @@ void TextEdit::_notification(int p_what) { for (int i = cursor.line; i >= 0; i--) { int from = i == cursor.line ? cursor.column - 2 : text[i].length() - 1; for (int j = from; j >= 0; j--) { - CharType cc = text[i][j]; + char32_t cc = text[i][j]; // Ignore any brackets inside a string. if (cc == '"' || cc == '\'') { - CharType quotation = cc; + char32_t quotation = cc; do { j--; if (!(j >= 0)) { @@ -1303,8 +1303,8 @@ void TextEdit::_notification(int p_what) { break; } - CharType cchar = ime_text[ofs]; - CharType next = ime_text[ofs + 1]; + char32_t cchar = ime_text[ofs]; + char32_t next = ime_text[ofs + 1]; int im_char_width = cache.font->get_char_size(cchar, next).width; if ((char_ofs + char_margin + im_char_width) >= xmargin_end) { @@ -1399,8 +1399,8 @@ void TextEdit::_notification(int p_what) { break; } - CharType cchar = ime_text[ofs]; - CharType next = ime_text[ofs + 1]; + char32_t cchar = ime_text[ofs]; + char32_t next = ime_text[ofs + 1]; int im_char_width = cache.font->get_char_size(cchar, next).width; if ((char_ofs + char_margin + im_char_width) >= xmargin_end) { @@ -1661,12 +1661,12 @@ void TextEdit::_notification(int p_what) { } } -void TextEdit::_consume_pair_symbol(CharType ch) { +void TextEdit::_consume_pair_symbol(char32_t ch) { int cursor_position_to_move = cursor_get_column() + 1; - CharType ch_single[2] = { ch, 0 }; - CharType ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 }; - CharType ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 }; + char32_t ch_single[2] = { ch, 0 }; + char32_t ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 }; + char32_t ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 }; if (is_selection_active()) { int new_column, new_line; @@ -1771,8 +1771,8 @@ void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column bool remove_right_symbol = false; if (cursor.column < text[cursor.line].length() && cursor.column > 0) { - CharType left_char = text[cursor.line][cursor.column - 1]; - CharType right_char = text[cursor.line][cursor.column]; + char32_t left_char = text[cursor.line][cursor.column - 1]; + char32_t right_char = text[cursor.line][cursor.column]; if (right_char == _get_right_pair_symbol(left_char)) { remove_right_symbol = true; @@ -2540,7 +2540,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { if (k->get_unicode() > 32) { _reset_caret_blink_timer(); - const CharType chr[2] = { (CharType)k->get_unicode(), 0 }; + const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 }; if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { _consume_pair_symbol(chr[0]); } else { @@ -2784,7 +2784,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { } // No need to move the brace below if we are not taking the text with us. - char closing_char = _get_right_pair_symbol(indent_char); + char32_t closing_char = _get_right_pair_symbol(indent_char); if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) { brace_indent = true; ins += "\n" + ins.substr(1, ins.length() - 2); @@ -3312,7 +3312,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { // Compute whitespace symbols seq length. int current_line_whitespace_len = 0; while (current_line_whitespace_len < text[cursor.line].length()) { - CharType c = text[cursor.line][current_line_whitespace_len]; + char32_t c = text[cursor.line][current_line_whitespace_len]; if (c != '\t' && c != ' ') { break; } @@ -3458,7 +3458,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { int current_line_whitespace_len = 0; while (current_line_whitespace_len < text[cursor.line].length()) { - CharType c = text[cursor.line][current_line_whitespace_len]; + char32_t c = text[cursor.line][current_line_whitespace_len]; if (c != '\t' && c != ' ') break; current_line_whitespace_len++; @@ -3624,7 +3624,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { } } - const CharType chr[2] = { (CharType)k->get_unicode(), 0 }; + const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 }; if (completion_hint != "" && k->get_unicode() == ')') { completion_hint = ""; @@ -4251,7 +4251,7 @@ Vector TextEdit::get_wrap_rows_text(int p_line) const { } while (col < line_text.length()) { - CharType c = line_text[col]; + char32_t c = line_text[col]; int w = text.get_char_width(c, line_text[col + 1], px + word_px); int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0); @@ -6115,9 +6115,9 @@ void TextEdit::_confirm_completion() { // When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket. String line = text[cursor.line]; - CharType next_char = line[cursor.column]; - CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1]; - CharType last_completion_char_display = completion_current.display[completion_current.display.length() - 1]; + char32_t next_char = line[cursor.column]; + char32_t last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1]; + char32_t last_completion_char_display = completion_current.display[completion_current.display.length() - 1]; if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) { _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); @@ -6161,7 +6161,7 @@ void TextEdit::_cancel_completion() { update(); } -static bool _is_completable(CharType c) { +static bool _is_completable(char32_t c) { return !_is_symbol(c) || c == '"' || c == '\''; } @@ -6292,14 +6292,14 @@ void TextEdit::_update_completion_candidates() { String display_lower = option.display.to_lower(); - const CharType *ssq = &s[0]; - const CharType *ssq_lower = &s_lower[0]; + const char32_t *ssq = &s[0]; + const char32_t *ssq_lower = &s_lower[0]; - const CharType *tgt = &option.display[0]; - const CharType *tgt_lower = &display_lower[0]; + const char32_t *tgt = &option.display[0]; + const char32_t *tgt_lower = &display_lower[0]; - const CharType *ssq_last_tgt = nullptr; - const CharType *ssq_lower_last_tgt = nullptr; + const char32_t *ssq_last_tgt = nullptr; + const char32_t *ssq_lower_last_tgt = nullptr; for (; *tgt; tgt++, tgt_lower++) { if (*ssq == *tgt) { @@ -6416,7 +6416,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { int beg, end; if (select_word(s, col, beg, end)) { bool inside_quotes = false; - CharType selected_quote = '\0'; + char32_t selected_quote = '\0'; int qbegin = 0, qend = 0; for (int i = 0; i < s.length(); i++) { if (s[i] == '"' || s[i] == '\'') { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index a6bc9963cc..70d7365d71 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -79,7 +79,7 @@ public: void set_font(const Ref &p_font); int get_line_width(int p_line) const; int get_max_width(bool p_exclude_hidden = false) const; - int get_char_width(CharType c, CharType next_c, int px) const; + int get_char_width(char32_t c, char32_t next_c, int px) const; void set_line_wrap_amount(int p_line, int p_wrap_amount) const; int get_line_wrap_amount(int p_line) const; void set(int p_line, const String &p_text); @@ -488,7 +488,7 @@ protected: void _gui_input(const Ref &p_gui_input); void _notification(int p_what); - void _consume_pair_symbol(CharType ch); + void _consume_pair_symbol(char32_t ch); void _consume_backspace_for_pair_symbol(int prev_line, int prev_column); static void _bind_methods(); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index d6d1134cc9..5cd45ea408 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -939,9 +939,9 @@ float CanvasItem::draw_char(const Ref &p_font, const Point2 &p_pos, const ERR_FAIL_COND_V(p_font.is_null(), 0); if (p_font->has_outline()) { - p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], Color(1, 1, 1), true); + p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], Color(1, 1, 1), true); } - return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], p_modulate); + return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_modulate); } void CanvasItem::_notify_transform(CanvasItem *p_node) { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6b304c03d2..e7753089c7 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1094,7 +1094,7 @@ String increase_numeric_string(const String &s) { if (!carry) { break; } - CharType n = s[i]; + char32_t n = s[i]; if (n == '9') { // keep carry as true: 9 + 1 res[i] = '0'; } else { @@ -1155,7 +1155,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co String name_string = name; String nums; for (int i = name_string.length() - 1; i >= 0; i--) { - CharType n = name_string[i]; + char32_t n = name_string[i]; if (n >= '0' && n <= '9') { nums = String::chr(name_string[i]) + nums; } else { diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 99f87dd6ed..bc983c1d7e 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -239,7 +239,7 @@ float DynamicFontAtSize::get_underline_thickness() const { return underline_thickness; } -const Pair DynamicFontAtSize::_find_char_with_font(CharType p_char, const Vector> &p_fallbacks) const { +const Pair DynamicFontAtSize::_find_char_with_font(char32_t p_char, const Vector> &p_fallbacks) const { const Character *chr = char_map.getptr(p_char); ERR_FAIL_COND_V(!chr, (Pair(nullptr, nullptr))); @@ -271,7 +271,7 @@ const Pair DynamicFon return Pair(chr, const_cast(this)); } -Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector> &p_fallbacks) const { +Size2 DynamicFontAtSize::get_char_size(char32_t p_char, char32_t p_next, const Vector> &p_fallbacks) const { if (!valid) { return Size2(1, 1); } @@ -297,7 +297,7 @@ String DynamicFontAtSize::get_available_chars() const { FT_ULong charcode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { if (charcode != 0) { - chars += CharType(charcode); + chars += char32_t(charcode); } charcode = FT_Get_Next_Char(face, charcode, &gindex); } @@ -305,7 +305,7 @@ String DynamicFontAtSize::get_available_chars() const { return chars; } -float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector> &p_fallbacks, bool p_advance_only, bool p_outline) const { +float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector> &p_fallbacks, bool p_advance_only, bool p_outline) const { if (!valid) { return 0; } @@ -560,7 +560,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap b return chr; } -DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_char) { +DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(char32_t p_char) { Character ret = Character::not_found(); if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0) { @@ -596,7 +596,7 @@ cleanup_stroker: return ret; } -void DynamicFontAtSize::_update_char(CharType p_char) { +void DynamicFontAtSize::_update_char(char32_t p_char) { if (char_map.has(p_char)) { return; } @@ -849,7 +849,7 @@ float DynamicFont::get_underline_thickness() const { return data_at_size->get_underline_thickness(); } -Size2 DynamicFont::get_char_size(CharType p_char, CharType p_next) const { +Size2 DynamicFont::get_char_size(char32_t p_char, char32_t p_next) const { if (!data_at_size.is_valid()) { return Size2(1, 1); } @@ -891,7 +891,7 @@ bool DynamicFont::has_outline() const { return outline_cache_id.outline_size > 0; } -float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { +float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const { const Ref &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size; if (!font_at_size.is_valid()) { diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index e8637e7e34..a881e21da8 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -159,17 +159,17 @@ class DynamicFontAtSize : public Reference { int y; }; - const Pair _find_char_with_font(CharType p_char, const Vector> &p_fallbacks) const; - Character _make_outline_char(CharType p_char); + const Pair _find_char_with_font(char32_t p_char, const Vector> &p_fallbacks) const; + Character _make_outline_char(char32_t p_char); TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height); Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance); static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count); static void _ft_stream_close(FT_Stream stream); - HashMap char_map; + HashMap char_map; - _FORCE_INLINE_ void _update_char(CharType p_char); + _FORCE_INLINE_ void _update_char(char32_t p_char); friend class DynamicFontData; Ref font; @@ -188,10 +188,10 @@ public: float get_underline_position() const; float get_underline_thickness() const; - Size2 get_char_size(CharType p_char, CharType p_next, const Vector> &p_fallbacks) const; + Size2 get_char_size(char32_t p_char, char32_t p_next, const Vector> &p_fallbacks) const; String get_available_chars() const; - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector> &p_fallbacks, bool p_advance_only = false, bool p_outline = false) const; + float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector> &p_fallbacks, bool p_advance_only = false, bool p_outline = false) const; void set_texture_flags(uint32_t p_flags); void update_oversampling(); @@ -277,14 +277,14 @@ public: virtual float get_underline_position() const override; virtual float get_underline_thickness() const override; - virtual Size2 get_char_size(CharType p_char, CharType p_next = 0) const override; + virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override; String get_available_chars() const; virtual bool is_distance_field_hint() const override; virtual bool has_outline() const override; - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; + virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; SelfList font_list; diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index ccab88a153..7cc39f661d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -125,7 +125,7 @@ void BitmapFont::_set_chars(const Vector &p_chars) { Vector BitmapFont::_get_chars() const { Vector chars; - const CharType *key = nullptr; + const char32_t *key = nullptr; while ((key = char_map.next(key))) { const Character *c = char_map.getptr(*key); @@ -272,7 +272,7 @@ Error BitmapFont::create_from_fnt(const String &p_file) { } } } else if (type == "char") { - CharType idx = 0; + char32_t idx = 0; if (keys.has("id")) { idx = keys["id"].to_int(); } @@ -313,7 +313,7 @@ Error BitmapFont::create_from_fnt(const String &p_file) { add_char(idx, texture, rect, ofs, advance); } else if (type == "kerning") { - CharType first = 0, second = 0; + char32_t first = 0, second = 0; int k = 0; if (keys.has("first")) { @@ -385,10 +385,10 @@ int BitmapFont::get_character_count() const { return char_map.size(); }; -Vector BitmapFont::get_char_keys() const { - Vector chars; +Vector BitmapFont::get_char_keys() const { + Vector chars; chars.resize(char_map.size()); - const CharType *ct = nullptr; + const char32_t *ct = nullptr; int count = 0; while ((ct = char_map.next(ct))) { chars.write[count++] = *ct; @@ -397,7 +397,7 @@ Vector BitmapFont::get_char_keys() const { return chars; }; -BitmapFont::Character BitmapFont::get_character(CharType p_char) const { +BitmapFont::Character BitmapFont::get_character(char32_t p_char) const { if (!char_map.has(p_char)) { ERR_FAIL_V(Character()); }; @@ -405,7 +405,7 @@ BitmapFont::Character BitmapFont::get_character(CharType p_char) const { return char_map[p_char]; }; -void BitmapFont::add_char(CharType p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { +void BitmapFont::add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { if (p_advance < 0) { p_advance = p_rect.size.width; } @@ -420,7 +420,7 @@ void BitmapFont::add_char(CharType p_char, int p_texture_idx, const Rect2 &p_rec char_map[p_char] = c; } -void BitmapFont::add_kerning_pair(CharType p_A, CharType p_B, int p_kerning) { +void BitmapFont::add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { KerningPairKey kpk; kpk.A = p_A; kpk.B = p_B; @@ -444,7 +444,7 @@ Vector BitmapFont::get_kerning_pair_keys() const { return ret; } -int BitmapFont::get_kerning_pair(CharType p_A, CharType p_B) const { +int BitmapFont::get_kerning_pair(char32_t p_A, char32_t p_B) const { KerningPairKey kpk; kpk.A = p_A; kpk.B = p_B; @@ -482,7 +482,7 @@ Size2 Font::get_string_size(const String &p_string) const { if (l == 0) { return Size2(0, get_height()); } - const CharType *sptr = &p_string[0]; + const char32_t *sptr = &p_string[0]; for (int i = 0; i < l; i++) { w += get_char_size(sptr[i], sptr[i + 1]).width; @@ -534,7 +534,7 @@ Ref BitmapFont::get_fallback() const { return fallback; } -float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { +float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const { const Character *c = char_map.getptr(p_char); if (!c) { @@ -556,7 +556,7 @@ float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_c return get_char_size(p_char, p_next).width; } -Size2 BitmapFont::get_char_size(CharType p_char, CharType p_next) const { +Size2 BitmapFont::get_char_size(char32_t p_char, char32_t p_next) const { const Character *c = char_map.getptr(p_char); if (!c) { diff --git a/scene/resources/font.h b/scene/resources/font.h index e6b296800b..c739520da3 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -49,7 +49,7 @@ public: virtual float get_underline_position() const = 0; virtual float get_underline_thickness() const = 0; - virtual Size2 get_char_size(CharType p_char, CharType p_next = 0) const = 0; + virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const = 0; Size2 get_string_size(const String &p_string) const; Size2 get_wordwrap_string_size(const String &p_string, float p_width) const; @@ -59,7 +59,7 @@ public: void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const; virtual bool has_outline() const { return false; } - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0; + virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0; void update_changes(); Font(); @@ -74,8 +74,8 @@ class FontDrawer { struct PendingDraw { RID canvas_item; Point2 pos; - CharType chr; - CharType next; + char32_t chr; + char32_t next; Color modulate; }; @@ -88,7 +88,7 @@ public: has_outline = p_font->has_outline(); } - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) { + float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1)) { if (has_outline) { PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate }; pending_draws.push_back(draw); @@ -137,7 +137,7 @@ public: }; private: - HashMap char_map; + HashMap char_map; Map kerning_map; float height; @@ -169,20 +169,20 @@ public: float get_underline_thickness() const override; void add_texture(const Ref &p_texture); - void add_char(CharType p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance = -1); + void add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance = -1); int get_character_count() const; - Vector get_char_keys() const; - Character get_character(CharType p_char) const; + Vector get_char_keys() const; + Character get_character(char32_t p_char) const; int get_texture_count() const; Ref get_texture(int p_idx) const; - void add_kerning_pair(CharType p_A, CharType p_B, int p_kerning); - int get_kerning_pair(CharType p_A, CharType p_B) const; + void add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning); + int get_kerning_pair(char32_t p_A, char32_t p_B) const; Vector get_kerning_pair_keys() const; - Size2 get_char_size(CharType p_char, CharType p_next = 0) const override; + Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override; void set_fallback(const Ref &p_fallback); Ref get_fallback() const; @@ -192,7 +192,7 @@ public: void set_distance_field_hint(bool p_distance_field); bool is_distance_field_hint() const override; - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; + float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; BitmapFont(); ~BitmapFont(); diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index 3500f6f01e..d7a08d9eb6 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -125,11 +125,11 @@ void SyntaxHighlighter::_bind_methods() { //////////////////////////////////////////////////////////////////////////////// -static bool _is_char(CharType c) { +static bool _is_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } -static bool _is_hex_symbol(CharType c) { +static bool _is_hex_symbol(char32_t c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } @@ -195,7 +195,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) { /* search the line */ bool match = true; - const CharType *start_key = color_regions[c].start_key.c_str(); + const char32_t *start_key = color_regions[c].start_key.get_data(); for (int k = 0; k < start_key_length; k++) { if (start_key[k] != str[from + k]) { match = false; @@ -232,7 +232,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) { /* search the line */ int region_end_index = -1; int end_key_length = color_regions[in_region].end_key.length(); - const CharType *end_key = color_regions[in_region].end_key.c_str(); + const char32_t *end_key = color_regions[in_region].end_key.get_data(); for (; from < line_length; from++) { if (line_length - from < end_key_length) { break; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 6992360df7..84b067d1e2 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -40,7 +40,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { if (slash == -1) { return false; } - int id = String::to_int(n.c_str(), slash); + int id = String::to_int(n.get_data(), slash); if (!tile_map.has(id)) { create_tile(id); @@ -216,7 +216,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { if (slash == -1) { return false; } - int id = String::to_int(n.c_str(), slash); + int id = String::to_int(n.get_data(), slash); ERR_FAIL_COND_V(!tile_map.has(id), false); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index a282f10402..e60f79f3a0 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -33,15 +33,15 @@ #include "core/print_string.h" #include "servers/rendering_server.h" -static bool _is_text_char(CharType c) { +static bool _is_text_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_number(CharType c) { +static bool _is_number(char32_t c) { return (c >= '0' && c <= '9'); } -static bool _is_hex(CharType c) { +static bool _is_hex(char32_t c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } @@ -334,7 +334,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { }; ShaderLanguage::Token ShaderLanguage::_get_token() { -#define GETCHAR(m_idx) (((char_idx + m_idx) < code.length()) ? code[char_idx + m_idx] : CharType(0)) +#define GETCHAR(m_idx) (((char_idx + m_idx) < code.length()) ? code[char_idx + m_idx] : char32_t(0)) while (true) { char_idx++; @@ -582,11 +582,11 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { break; } - str += CharType(GETCHAR(i)); + str += char32_t(GETCHAR(i)); i++; } - CharType last_char = str[str.length() - 1]; + char32_t last_char = str[str.length() - 1]; if (hexa_found) { //integer(hex) @@ -663,7 +663,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { String str; while (_is_text_char(GETCHAR(0))) { - str += CharType(GETCHAR(0)); + str += char32_t(GETCHAR(0)); char_idx++; } @@ -3869,7 +3869,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons break; } - const CharType *c = ident.ptr(); + const char32_t *c = ident.ptr(); for (int i = 0; i < l; i++) { switch (c[i]) { case 'r': @@ -3933,7 +3933,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons break; } - const CharType *c = ident.ptr(); + const char32_t *c = ident.ptr(); for (int i = 0; i < l; i++) { switch (c[i]) { case 'r': @@ -4000,7 +4000,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons break; } - const CharType *c = ident.ptr(); + const char32_t *c = ident.ptr(); for (int i = 0; i < l; i++) { switch (c[i]) { case 'r': @@ -7039,7 +7039,7 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat static int _get_first_ident_pos(const String &p_code) { int idx = 0; -#define GETCHAR(m_idx) (((idx + m_idx) < p_code.length()) ? p_code[idx + m_idx] : CharType(0)) +#define GETCHAR(m_idx) (((idx + m_idx) < p_code.length()) ? p_code[idx + m_idx] : char32_t(0)) while (true) { if (GETCHAR(0) == '/' && GETCHAR(1) == '/') { @@ -7300,7 +7300,7 @@ Error ShaderLanguage::complete(const String &p_code, const Mapfunctions[i].function->arguments[j].is_const) { @@ -7320,7 +7320,7 @@ Error ShaderLanguage::complete(const String &p_code, const Mapfunctions[i].function->arguments[j].name; if (j == completion_argument) { - calltip += CharType(0xFFFF); + calltip += char32_t(0xFFFF); } } @@ -7383,7 +7383,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map= 0 && i == out_arg) { @@ -7393,7 +7393,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map 0); + CHECK(godot_string_naturalnocasecmp_to(&s, &t) < 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNatvie String] hex_encode_buffer") { + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 }; + godot_string s = godot_string_hex_encode_buffer(u8str, 6); + CHECK(u32scmp(godot_string_get_data(&s), U"45e3818a8fe3") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Substr") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Killer Baby"); + t = godot_string_substr(&s, 3, 4); + CHECK(u32scmp(godot_string_get_data(&t), U"ler ") == 0); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Find") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Pretty Woman Woman"); + + godot_string_new_with_latin1_chars(&t, "Revenge of the Monster Truck"); + CHECK(godot_string_find(&s, &t) == -1); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "tty"); + CHECK(godot_string_find(&s, &t) == 3); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Wo"); + CHECK(godot_string_find_from(&s, &t, 9) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "man"); + CHECK(godot_string_rfind(&s, &t) == 15); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Find no case") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Pretty Whale Whale"); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_findn(&s, &t) == 7); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_findn_from(&s, &t, 9) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_rfindn(&s, &t) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Revenge of the Monster SawFish"); + CHECK(godot_string_findn(&s, &t) == -1); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Find MK") { + godot_packed_string_array keys; + godot_packed_string_array_new(&keys); + +#define PUSH_KEY(x) \ + { \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + godot_packed_string_array_push_back(&keys, &t); \ + godot_string_destroy(&t); \ + } + + PUSH_KEY("sty") + PUSH_KEY("tty") + PUSH_KEY("man") + + godot_string s; + godot_string_new_with_latin1_chars(&s, "Pretty Woman"); + godot_int key = 0; + + CHECK(godot_string_findmk(&s, &keys) == 3); + CHECK(godot_string_findmk_from_in_place(&s, &keys, 0, &key) == 3); + CHECK(key == 1); + + CHECK(godot_string_findmk_from(&s, &keys, 5) == 9); + CHECK(godot_string_findmk_from_in_place(&s, &keys, 5, &key) == 9); + CHECK(key == 2); + + godot_string_destroy(&s); + godot_packed_string_array_destroy(&keys); + +#undef PUSH_KEY +} + +TEST_CASE("[GDNatvie String] Find and replace") { + godot_string s, c, w; + godot_string_new_with_latin1_chars(&s, "Happy Birthday, Anna!"); + godot_string_new_with_latin1_chars(&c, "Birthday"); + godot_string_new_with_latin1_chars(&w, "Halloween"); + godot_string t = godot_string_replace(&s, &c, &w); + CHECK(u32scmp(godot_string_get_data(&t), U"Happy Halloween, Anna!") == 0); + godot_string_destroy(&s); + godot_string_destroy(&c); + godot_string_destroy(&w); + + godot_string_new_with_latin1_chars(&c, "H"); + godot_string_new_with_latin1_chars(&w, "W"); + s = godot_string_replace_first(&t, &c, &w); + godot_string_destroy(&t); + godot_string_destroy(&c); + godot_string_destroy(&w); + + CHECK(u32scmp(godot_string_get_data(&s), U"Wappy Halloween, Anna!") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Insertion") { + godot_string s, t, r, u; + godot_string_new_with_latin1_chars(&s, "Who is Frederic?"); + godot_string_new_with_latin1_chars(&t, "?"); + godot_string_new_with_latin1_chars(&r, " Chopin"); + + u = godot_string_insert(&s, godot_string_find(&s, &t), &r); + CHECK(u32scmp(godot_string_get_data(&u), U"Who is Frederic Chopin?") == 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); + godot_string_destroy(&r); + godot_string_destroy(&u); +} + +TEST_CASE("[GDNatvie String] Number to string") { + godot_string s; + s = godot_string_num(3.141593); + CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_with_decimals(3.141593, 3); + CHECK(u32scmp(godot_string_get_data(&s), U"3.142") == 0); + godot_string_destroy(&s); + + s = godot_string_num_real(3.141593); + CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_scientific(30000000); + CHECK(u32scmp(godot_string_get_data(&s), U"3e+07") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64(3141593, 10); + CHECK(u32scmp(godot_string_get_data(&s), U"3141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64(0xA141593, 16); + CHECK(u32scmp(godot_string_get_data(&s), U"a141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64_capitalized(0xA141593, 16, true); + CHECK(u32scmp(godot_string_get_data(&s), U"A141593") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] String to integer") { + static const wchar_t *wnums[4] = { L"1237461283", L"- 22", L"0", L" - 1123412" }; + static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" }; + static const int num[4] = { 1237461283, -22, 0, -1123412 }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK(godot_string_to_int(&s) == num[i]); + godot_string_destroy(&s); + + CHECK(godot_string_char_to_int(nums[i]) == num[i]); + CHECK(godot_string_wchar_to_int(wnums[i]) == num[i]); + } +} + +TEST_CASE("[GDNatvie String] Hex to integer") { + static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" }; + static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD }; + static const bool wo_prefix[4] = { false, true, true, true }; + static const bool w_prefix[4] = { true, false, true, false }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK((godot_string_hex_to_int_with_prefix(&s) == num[i]) == w_prefix[i]); + CHECK((godot_string_hex_to_int(&s) == num[i]) == wo_prefix[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNatvie String] String to float") { + static const wchar_t *wnums[4] = { L"-12348298412.2", L"0.05", L"2.0002", L" -0.0001" }; + static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" }; + static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK(!(ABS(godot_string_to_float(&s) - num[i]) > 0.00001)); + godot_string_destroy(&s); + + CHECK(!(ABS(godot_string_char_to_float(nums[i]) - num[i]) > 0.00001)); + CHECK(!(ABS(godot_string_wchar_to_float(wnums[i], nullptr) - num[i]) > 0.00001)); + } +} + +TEST_CASE("[GDNatvie String] CamelCase to underscore") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "TestTestStringGD"); + + t = godot_string_camelcase_to_underscore(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"Test_Test_String_GD") == 0); + godot_string_destroy(&t); + + t = godot_string_camelcase_to_underscore_lowercased(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"test_test_string_gd") == 0); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Slicing") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus"); + godot_string_new_with_latin1_chars(&c, ","); + + const char32_t *slices[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" }; + for (int i = 0; i < godot_string_get_slice_count(&s, &c); i++) { + godot_string t; + t = godot_string_get_slice(&s, &c, i); + CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0); + godot_string_destroy(&t); + + t = godot_string_get_slicec(&s, U',', i); + CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0); + godot_string_destroy(&t); + } + + godot_string_destroy(&c); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Splitting") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus"); + godot_string_new_with_latin1_chars(&c, ","); + + godot_packed_string_array l; + + const char32_t *slices_l[3] = { U"Mars", U"Jupiter", U"Saturn,Uranus" }; + const char32_t *slices_r[3] = { U"Mars,Jupiter", U"Saturn", U"Uranus" }; + + l = godot_string_split_with_maxsplit(&s, &c, true, 2); + CHECK(godot_packed_string_array_size(&l) == 3); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_l[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + + l = godot_string_rsplit_with_maxsplit(&s, &c, true, 2); + CHECK(godot_packed_string_array_size(&l) == 3); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_r[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + godot_string_destroy(&s); + + godot_string_new_with_latin1_chars(&s, "Mars Jupiter Saturn Uranus"); + const char32_t *slices_s[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" }; + l = godot_string_split_spaces(&s); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_s[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + godot_string_destroy(&s); + + godot_string c1, c2; + godot_string_new_with_latin1_chars(&c1, ";"); + godot_string_new_with_latin1_chars(&c2, " "); + + godot_string_new_with_latin1_chars(&s, "1.2;2.3 4.5"); + const double slices_d[3] = { 1.2, 2.3, 4.5 }; + + godot_packed_float32_array lf = godot_string_split_floats_allow_empty(&s, &c1); + CHECK(godot_packed_float32_array_size(&lf) == 2); + for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) { + CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001); + } + godot_packed_float32_array_destroy(&lf); + + godot_packed_string_array keys; + godot_packed_string_array_new(&keys); + godot_packed_string_array_push_back(&keys, &c1); + godot_packed_string_array_push_back(&keys, &c2); + + lf = godot_string_split_floats_mk_allow_empty(&s, &keys); + CHECK(godot_packed_float32_array_size(&lf) == 3); + for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) { + CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001); + } + godot_packed_float32_array_destroy(&lf); + + godot_string_destroy(&s); + godot_string_new_with_latin1_chars(&s, "1;2 4"); + const int slices_i[3] = { 1, 2, 4 }; + + godot_packed_int32_array li = godot_string_split_ints_allow_empty(&s, &c1); + CHECK(godot_packed_int32_array_size(&li) == 2); + for (int i = 0; i < godot_packed_int32_array_size(&li); i++) { + CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]); + } + godot_packed_int32_array_destroy(&li); + + li = godot_string_split_ints_mk_allow_empty(&s, &keys); + CHECK(godot_packed_int32_array_size(&li) == 3); + for (int i = 0; i < godot_packed_int32_array_size(&li); i++) { + CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]); + } + godot_packed_int32_array_destroy(&li); + + godot_string_destroy(&s); + godot_string_destroy(&c); + godot_string_destroy(&c1); + godot_string_destroy(&c2); + godot_packed_string_array_destroy(&keys); +} + +TEST_CASE("[GDNatvie String] Erasing") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Josephine is such a cute girl!"); + godot_string_new_with_latin1_chars(&t, "cute "); + + godot_string_erase(&s, godot_string_find(&s, &t), godot_string_length(&t)); + + CHECK(u32scmp(godot_string_get_data(&s), U"Josephine is such a girl!") == 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +struct test_27_data { + char const *data; + char const *part; + bool expected; +}; + +TEST_CASE("[GDNatvie String] Begins with") { + test_27_data tc[] = { + { "res://foobar", "res://", true }, + { "res", "res://", false }, + { "abc", "abc", true } + }; + size_t count = sizeof(tc) / sizeof(tc[0]); + bool state = true; + for (size_t i = 0; state && i < count; ++i) { + godot_string s; + godot_string_new_with_latin1_chars(&s, tc[i].data); + + state = godot_string_begins_with_char_array(&s, tc[i].part) == tc[i].expected; + if (state) { + godot_string t; + godot_string_new_with_latin1_chars(&t, tc[i].part); + state = godot_string_begins_with(&s, &t) == tc[i].expected; + godot_string_destroy(&t); + } + godot_string_destroy(&s); + + CHECK(state); + if (!state) { + break; + } + }; + CHECK(state); +} + +TEST_CASE("[GDNatvie String] Ends with") { + test_27_data tc[] = { + { "res://foobar", "foobar", true }, + { "res", "res://", false }, + { "abc", "abc", true } + }; + size_t count = sizeof(tc) / sizeof(tc[0]); + bool state = true; + for (size_t i = 0; state && i < count; ++i) { + godot_string s; + godot_string_new_with_latin1_chars(&s, tc[i].data); + + state = godot_string_ends_with_char_array(&s, tc[i].part) == tc[i].expected; + if (state) { + godot_string t; + godot_string_new_with_latin1_chars(&t, tc[i].part); + state = godot_string_ends_with(&s, &t) == tc[i].expected; + godot_string_destroy(&t); + } + godot_string_destroy(&s); + + CHECK(state); + if (!state) { + break; + } + }; + CHECK(state); +} + +TEST_CASE("[GDNatvie String] format") { + godot_string value_format, t; + godot_string_new_with_latin1_chars(&value_format, "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""); + + godot_variant key_v, val_v; + godot_dictionary value_dictionary; + godot_dictionary_new(&value_dictionary); + + godot_string_new_with_latin1_chars(&t, "red"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_int(&val_v, 10); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "green"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_int(&val_v, 20); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "blue"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "bla"); + godot_variant_new_string(&val_v, &t); + godot_string_destroy(&t); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "alpha"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_real(&val_v, 0.4); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_variant dict_v; + godot_variant_new_dictionary(&dict_v, &value_dictionary); + godot_string s = godot_string_format_with_custom_placeholder(&value_format, &dict_v, "$_"); + + CHECK(u32scmp(godot_string_get_data(&s), U"red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"") == 0); + + godot_dictionary_destroy(&value_dictionary); + godot_string_destroy(&s); + godot_variant_destroy(&dict_v); + godot_string_destroy(&value_format); +} + +TEST_CASE("[GDNatvie String] sprintf") { + //godot_string GDAPI (const godot_string *p_self, const godot_array *p_values, godot_bool *p_error); + godot_string format, output; + godot_array args; + bool error; + +#define ARRAY_PUSH_STRING(x) \ + { \ + godot_variant v; \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + godot_variant_new_string(&v, &t); \ + godot_string_destroy(&t); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + +#define ARRAY_PUSH_INT(x) \ + { \ + godot_variant v; \ + godot_variant_new_int(&v, x); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + +#define ARRAY_PUSH_REAL(x) \ + { \ + godot_variant v; \ + godot_variant_new_real(&v, x); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + + godot_array_new(&args); + + // %% + godot_string_new_with_latin1_chars(&format, "fish %% frog"); + godot_array_clear(&args); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish % frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + //////// INTS + + // Int + godot_string_new_with_latin1_chars(&format, "fish %d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int left padded with zeroes. + godot_string_new_with_latin1_chars(&format, "fish %05d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 00005 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int left padded with spaces. + godot_string_new_with_latin1_chars(&format, "fish %5d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int right padded with spaces. + godot_string_new_with_latin1_chars(&format, "fish %-5d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int with sign (positive). + godot_string_new_with_latin1_chars(&format, "fish %+d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish +5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Negative int. + godot_string_new_with_latin1_chars(&format, "fish %d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(-5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish -5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Hex (lower) + godot_string_new_with_latin1_chars(&format, "fish %x frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(45); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 2d frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Hex (upper) + godot_string_new_with_latin1_chars(&format, "fish %X frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(45); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 2D frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Octal + godot_string_new_with_latin1_chars(&format, "fish %o frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 143 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + ////// REALS + + // Real + godot_string_new_with_latin1_chars(&format, "fish %f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real left-padded + godot_string_new_with_latin1_chars(&format, "fish %11f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real right-padded + godot_string_new_with_latin1_chars(&format, "fish %-11f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real given int. + godot_string_new_with_latin1_chars(&format, "fish %f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.000000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with sign (positive). + godot_string_new_with_latin1_chars(&format, "fish %+f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish +99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with 1 decimals. + godot_string_new_with_latin1_chars(&format, "fish %.1f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 100.0 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with 12 decimals. + godot_string_new_with_latin1_chars(&format, "fish %.12f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000000000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with no decimals. + godot_string_new_with_latin1_chars(&format, "fish %.f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 100 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + /////// Strings. + + // String + godot_string_new_with_latin1_chars(&format, "fish %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // String left-padded + godot_string_new_with_latin1_chars(&format, "fish %10s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // String right-padded + godot_string_new_with_latin1_chars(&format, "fish %-10s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Characters + + // Character as string. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("A"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character as int. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(65); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Dynamic width + + // String dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*s frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + REQUIRE(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_INT(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + REQUIRE(u32scmp(godot_string_get_data(&output), U"fish 99 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Float dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*.*f frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_INT(3); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Errors + + // More formats than arguments. + godot_string_new_with_latin1_chars(&format, "fish %s %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"not enough arguments for format string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // More arguments than formats. + godot_string_new_with_latin1_chars(&format, "fish %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("hello"); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"not all arguments converted during string formatting") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Incomplete format. + godot_string_new_with_latin1_chars(&format, "fish %10"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"incomplete format") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Bad character in format string + godot_string_new_with_latin1_chars(&format, "fish %&f frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"unsupported format character") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Too many decimals. + godot_string_new_with_latin1_chars(&format, "fish %2.2.2f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"too many decimal points in format") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // * not a number + godot_string_new_with_latin1_chars(&format, "fish %*f frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"* wants number") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character too long. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("sc"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character bad type. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + godot_array t; + godot_array_new(&t); + godot_variant v; + godot_variant_new_array(&v, &t); + godot_array_destroy(&t); + godot_array_push_back(&args, &v); + godot_variant_destroy(&v); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + godot_array_destroy(&args); +#undef ARRAY_PUSH_INT +#undef ARRAY_PUSH_REAL +#undef ARRAY_PUSH_STRING +} + +TEST_CASE("[GDNatvie String] is_numeric") { +#define IS_NUM_TEST(x, r) \ + { \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + CHECK(godot_string_is_numeric(&t) == r); \ + godot_string_destroy(&t); \ + } + + IS_NUM_TEST("12", true); + IS_NUM_TEST("1.2", true); + IS_NUM_TEST("AF", false); + IS_NUM_TEST("-12", true); + IS_NUM_TEST("-1.2", true); + +#undef IS_NUM_TEST +} + +TEST_CASE("[GDNatvie String] pad") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "test"); + godot_string_new_with_latin1_chars(&c, "x"); + + godot_string l = godot_string_lpad_with_custom_character(&s, 10, &c); + CHECK(u32scmp(godot_string_get_data(&l), U"xxxxxxtest") == 0); + godot_string_destroy(&l); + + godot_string r = godot_string_rpad_with_custom_character(&s, 10, &c); + CHECK(u32scmp(godot_string_get_data(&r), U"testxxxxxx") == 0); + godot_string_destroy(&r); + + godot_string_destroy(&s); + godot_string_destroy(&c); + + godot_string_new_with_latin1_chars(&s, "10.10"); + c = godot_string_pad_decimals(&s, 4); + CHECK(u32scmp(godot_string_get_data(&c), U"10.1000") == 0); + godot_string_destroy(&c); + c = godot_string_pad_zeros(&s, 4); + CHECK(u32scmp(godot_string_get_data(&c), U"0010.10") == 0); + godot_string_destroy(&c); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] is_subsequence_of") { + godot_string a, t; + godot_string_new_with_latin1_chars(&a, "is subsequence of"); + + godot_string_new_with_latin1_chars(&t, "sub"); + CHECK(godot_string_is_subsequence_of(&t, &a)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Sub"); + CHECK(!godot_string_is_subsequence_of(&t, &a)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Sub"); + CHECK(godot_string_is_subsequence_ofi(&t, &a)); + godot_string_destroy(&t); + + godot_string_destroy(&a); +} + +TEST_CASE("[GDNatvie String] match") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "*.png"); + + godot_string_new_with_latin1_chars(&t, "img1.png"); + CHECK(godot_string_match(&t, &s)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "img1.jpeg"); + CHECK(!godot_string_match(&t, &s)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "img1.Png"); + CHECK(!godot_string_match(&t, &s)); + CHECK(godot_string_matchn(&t, &s)); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] IPVX address to string") { + godot_string ip; + + godot_string_new_with_latin1_chars(&ip, "192.168.0.1"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "192.368.0.1"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85j3:0000:0000:8a2e:0370:7334"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85f345:0000:0000:8a2e:0370:7334"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8::0:8a2e:370:7334"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "::ffff:192.168.0.1"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); +} + +TEST_CASE("[GDNatvie String] Capitalize against many strings") { +#define CAP_TEST(i, o) \ + godot_string_new_with_latin1_chars(&input, i); \ + godot_string_new_with_latin1_chars(&output, o); \ + test = godot_string_capitalize(&input); \ + CHECK(u32scmp(godot_string_get_data(&output), godot_string_get_data(&test)) == 0); \ + godot_string_destroy(&input); \ + godot_string_destroy(&output); \ + godot_string_destroy(&test); + + godot_string input, output, test; + + CAP_TEST("bytes2var", "Bytes 2 Var"); + CAP_TEST("linear2db", "Linear 2 Db"); + CAP_TEST("vector3", "Vector 3"); + CAP_TEST("sha256", "Sha 256"); + CAP_TEST("2db", "2 Db"); + CAP_TEST("PascalCase", "Pascal Case"); + CAP_TEST("PascalPascalCase", "Pascal Pascal Case"); + CAP_TEST("snake_case", "Snake Case"); + CAP_TEST("snake_snake_case", "Snake Snake Case"); + CAP_TEST("sha256sum", "Sha 256 Sum"); + CAP_TEST("cat2dog", "Cat 2 Dog"); + CAP_TEST("function(name)", "Function(name)"); + CAP_TEST("snake_case_function(snake_case_arg)", "Snake Case Function(snake Case Arg)"); + CAP_TEST("snake_case_function( snake_case_arg )", "Snake Case Function( Snake Case Arg )"); + +#undef CAP_TEST +} + +TEST_CASE("[GDNatvie String] lstrip and rstrip") { +#define LSTRIP_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_latin1_chars(&xx, x); \ + godot_string_new_with_latin1_chars(&yy, y); \ + godot_string_new_with_latin1_chars(&zz, z); \ + rr = godot_string_lstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define RSTRIP_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_latin1_chars(&xx, x); \ + godot_string_new_with_latin1_chars(&yy, y); \ + godot_string_new_with_latin1_chars(&zz, z); \ + rr = godot_string_rstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define LSTRIP_UTF8_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_utf8_chars(&xx, x); \ + godot_string_new_with_utf8_chars(&yy, y); \ + godot_string_new_with_utf8_chars(&zz, z); \ + rr = godot_string_lstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define RSTRIP_UTF8_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_utf8_chars(&xx, x); \ + godot_string_new_with_utf8_chars(&yy, y); \ + godot_string_new_with_utf8_chars(&zz, z); \ + rr = godot_string_rstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + + bool state = true; + + // strip none + LSTRIP_TEST("abc", "", "abc"); + RSTRIP_TEST("abc", "", "abc"); + // strip one + LSTRIP_TEST("abc", "a", "bc"); + RSTRIP_TEST("abc", "c", "ab"); + // strip lots + LSTRIP_TEST("bababbababccc", "ab", "ccc"); + RSTRIP_TEST("aaabcbcbcbbcbbc", "cb", "aaa"); + // strip empty string + LSTRIP_TEST("", "", ""); + RSTRIP_TEST("", "", ""); + // strip to empty string + LSTRIP_TEST("abcabcabc", "bca", ""); + RSTRIP_TEST("abcabcabc", "bca", ""); + // don't strip wrong end + LSTRIP_TEST("abc", "c", "abc"); + LSTRIP_TEST("abca", "a", "bca"); + RSTRIP_TEST("abc", "a", "abc"); + RSTRIP_TEST("abca", "a", "abc"); + // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5) + // and the same second as "ÿ" (\u00ff) + LSTRIP_UTF8_TEST("¿", "µÿ", "¿"); + RSTRIP_UTF8_TEST("¿", "µÿ", "¿"); + LSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "¿ÿ"); + RSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "µ¿"); + + // the above tests repeated with additional superfluous strip chars + + // strip none + LSTRIP_TEST("abc", "qwjkl", "abc"); + RSTRIP_TEST("abc", "qwjkl", "abc"); + // strip one + LSTRIP_TEST("abc", "qwajkl", "bc"); + RSTRIP_TEST("abc", "qwcjkl", "ab"); + // strip lots + LSTRIP_TEST("bababbababccc", "qwabjkl", "ccc"); + RSTRIP_TEST("aaabcbcbcbbcbbc", "qwcbjkl", "aaa"); + // strip empty string + LSTRIP_TEST("", "qwjkl", ""); + RSTRIP_TEST("", "qwjkl", ""); + // strip to empty string + LSTRIP_TEST("abcabcabc", "qwbcajkl", ""); + RSTRIP_TEST("abcabcabc", "qwbcajkl", ""); + // don't strip wrong end + LSTRIP_TEST("abc", "qwcjkl", "abc"); + LSTRIP_TEST("abca", "qwajkl", "bca"); + RSTRIP_TEST("abc", "qwajkl", "abc"); + RSTRIP_TEST("abca", "qwajkl", "abc"); + // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5) + // and the same second as "ÿ" (\u00ff) + LSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿"); + RSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿"); + LSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "¿ÿ"); + RSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "µ¿"); + + CHECK(state); + +#undef LSTRIP_TEST +#undef RSTRIP_TEST +#undef LSTRIP_UTF8_TEST +#undef RSTRIP_UTF8_TEST +} + +TEST_CASE("[GDNatvie String] Cyrillic to_lower()") { + godot_string upper, lower, test; + godot_string_new_with_utf8_chars(&upper, "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"); + godot_string_new_with_utf8_chars(&lower, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"); + + test = godot_string_to_lower(&upper); + + CHECK((u32scmp(godot_string_get_data(&test), godot_string_get_data(&lower)) == 0)); + + godot_string_destroy(&upper); + godot_string_destroy(&lower); + godot_string_destroy(&test); +} + +TEST_CASE("[GDNatvie String] Count and countn functionality") { +#define COUNT_TEST(x, y, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_count(&s, &t, 0, 0) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTR_TEST(x, y, a, b, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_count(&s, &t, a, b) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTN_TEST(x, y, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_countn(&s, &t, 0, 0) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTNR_TEST(x, y, a, b, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_countn(&s, &t, a, b) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + bool state = true; + + COUNT_TEST("", "Test", 0); + COUNT_TEST("Test", "", 0); + COUNT_TEST("Test", "test", 0); + COUNT_TEST("Test", "TEST", 0); + COUNT_TEST("TEST", "TEST", 1); + COUNT_TEST("Test", "Test", 1); + COUNT_TEST("aTest", "Test", 1); + COUNT_TEST("Testa", "Test", 1); + COUNT_TEST("TestTestTest", "Test", 3); + COUNT_TEST("TestTestTest", "TestTest", 1); + COUNT_TEST("TestGodotTestGodotTestGodot", "Test", 3); + + COUNTR_TEST("TestTestTestTest", "Test", 4, 8, 1); + COUNTR_TEST("TestTestTestTest", "Test", 4, 12, 2); + COUNTR_TEST("TestTestTestTest", "Test", 4, 16, 3); + COUNTR_TEST("TestTestTestTest", "Test", 4, 0, 3); + + COUNTN_TEST("Test", "test", 1); + COUNTN_TEST("Test", "TEST", 1); + COUNTN_TEST("testTest-Testatest", "tEst", 4); + COUNTNR_TEST("testTest-TeStatest", "tEsT", 4, 16, 2); + + CHECK(state); + +#undef COUNT_TEST +#undef COUNTR_TEST +#undef COUNTN_TEST +#undef COUNTNR_TEST +} + +TEST_CASE("[GDNatvie String] Bigrams") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "abcd"); + godot_packed_string_array bigr = godot_string_bigrams(&s); + godot_string_destroy(&s); + + CHECK(godot_packed_string_array_size(&bigr) == 3); + + t = godot_packed_string_array_get(&bigr, 0); + CHECK(u32scmp(godot_string_get_data(&t), U"ab") == 0); + godot_string_destroy(&t); + + t = godot_packed_string_array_get(&bigr, 1); + CHECK(u32scmp(godot_string_get_data(&t), U"bc") == 0); + godot_string_destroy(&t); + + t = godot_packed_string_array_get(&bigr, 2); + CHECK(u32scmp(godot_string_get_data(&t), U"cd") == 0); + godot_string_destroy(&t); + + godot_packed_string_array_destroy(&bigr); +} + +TEST_CASE("[GDNatvie String] c-escape/unescape") { + godot_string s; + godot_string_new_with_latin1_chars(&s, "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\""); + godot_string t = godot_string_c_escape(&s); + godot_string u = godot_string_c_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] dedent") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, " aaa\n bbb"); + godot_string_new_with_latin1_chars(&t, "aaa\nbbb"); + godot_string u = godot_string_dedent(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Path functions") { + static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" }; + static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" }; + static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" }; + static const char *ext[4] = { "tscn", "xscn", "scn", "doc" }; + static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" }; + static const bool abs[4] = { true, true, false, false }; + + for (int i = 0; i < 4; i++) { + godot_string s, t, u, f; + godot_string_new_with_latin1_chars(&s, path[i]); + + t = godot_string_get_base_dir(&s); + godot_string_new_with_latin1_chars(&u, base_dir[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_basename(&s); + godot_string_new_with_latin1_chars(&u, base_name[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_extension(&s); + godot_string_new_with_latin1_chars(&u, ext[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_file(&s); + godot_string_new_with_latin1_chars(&u, file[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string s_simp; + s_simp = godot_string_simplify_path(&s); + t = godot_string_get_base_dir(&s_simp); + godot_string_new_with_latin1_chars(&u, file[i]); + f = godot_string_plus_file(&t, &u); + CHECK(u32scmp(godot_string_get_data(&f), godot_string_get_data(&s_simp)) == 0); + godot_string_destroy(&f); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s_simp); + + CHECK(godot_string_is_abs_path(&s) == abs[i]); + CHECK(godot_string_is_rel_path(&s) != abs[i]); + + godot_string_destroy(&s); + } + + static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" }; + static const bool valid[3] = { true, false, false }; + for (int i = 0; i < 3; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, file_name[i]); + CHECK(godot_string_is_valid_filename(&s) == valid[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNatvie String] hash") { + godot_string a, b, c; + godot_string_new_with_latin1_chars(&a, "Test"); + godot_string_new_with_latin1_chars(&b, "Test"); + godot_string_new_with_latin1_chars(&c, "West"); + CHECK(godot_string_hash(&a) == godot_string_hash(&b)); + CHECK(godot_string_hash(&a) != godot_string_hash(&c)); + + CHECK(godot_string_hash64(&a) == godot_string_hash64(&b)); + CHECK(godot_string_hash64(&a) != godot_string_hash64(&c)); + + godot_string_destroy(&a); + godot_string_destroy(&b); + godot_string_destroy(&c); +} + +TEST_CASE("[GDNatvie String] http_escape/unescape") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'"); + godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3A%27docs%27"); + + u = godot_string_http_escape(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + u = godot_string_http_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNatvie String] percent_encode/decode") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'"); + godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3a%27docs%27"); + + u = godot_string_percent_encode(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + u = godot_string_percent_decode(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNatvie String] xml_escape/unescape") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\"Test\" "); + + t = godot_string_xml_escape_with_quotes(&s); + u = godot_string_xml_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_xml_escape(&s); + u = godot_string_xml_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Strip escapes") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\t\tTest Test\r\n Test"); + godot_string_new_with_latin1_chars(&t, "Test Test Test"); + + u = godot_string_strip_escapes(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Strip edges") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\t Test Test "); + + godot_string_new_with_latin1_chars(&t, "Test Test "); + u = godot_string_strip_edges(&s, true, false); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "\t Test Test"); + u = godot_string_strip_edges(&s, false, true); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Test Test"); + u = godot_string_strip_edges(&s, true, true); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Similarity") { + godot_string a, b, c; + godot_string_new_with_latin1_chars(&a, "Test"); + godot_string_new_with_latin1_chars(&b, "West"); + godot_string_new_with_latin1_chars(&c, "Toad"); + + CHECK(godot_string_similarity(&a, &b) > godot_string_similarity(&a, &c)); + + godot_string_destroy(&a); + godot_string_destroy(&b); + godot_string_destroy(&c); +} + +TEST_CASE("[GDNatvie String] Trim") { + godot_string s, t, u, p; + godot_string_new_with_latin1_chars(&s, "aaaTestbbb"); + + godot_string_new_with_latin1_chars(&p, "aaa"); + godot_string_new_with_latin1_chars(&t, "Testbbb"); + u = godot_string_trim_prefix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&p); + + godot_string_new_with_latin1_chars(&p, "bbb"); + godot_string_new_with_latin1_chars(&t, "aaaTest"); + u = godot_string_trim_suffix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&p); + + godot_string_new_with_latin1_chars(&p, "Test"); + u = godot_string_trim_suffix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&p); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Right/Left") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "aaaTestbbb"); + // ^ + + godot_string_new_with_latin1_chars(&t, "tbbb"); + u = godot_string_right(&s, 6); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "aaaTes"); + u = godot_string_left(&s, 6); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNatvie String] Repeat") { + godot_string t, u; + godot_string_new_with_latin1_chars(&t, "ab"); + + u = godot_string_repeat(&t, 4); + CHECK(u32scmp(godot_string_get_data(&u), U"abababab") == 0); + godot_string_destroy(&u); + + godot_string_destroy(&t); +} + +TEST_CASE("[GDNatvie String] SHA1/SHA256/MD5") { + godot_string s, t, sha1, sha256, md5; + godot_string_new_with_latin1_chars(&s, "Godot"); + godot_string_new_with_latin1_chars(&sha1, "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201"); + static uint8_t sha1_buf[20] = { + 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3, + 0x9D, 0xC2, 0xD2, 0x01 + }; + godot_string_new_with_latin1_chars(&sha256, "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8"); + static uint8_t sha256_buf[32] = { + 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF, + 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8 + }; + godot_string_new_with_latin1_chars(&md5, "4a336d087aeb0390da10ee2ea7cb87f8"); + static uint8_t md5_buf[16] = { + 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8 + }; + + godot_packed_byte_array buf = godot_string_sha1_buffer(&s); + CHECK(memcmp(sha1_buf, godot_packed_byte_array_ptr(&buf), 20) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_sha1_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha1)) == 0); + godot_string_destroy(&t); + + buf = godot_string_sha256_buffer(&s); + CHECK(memcmp(sha256_buf, godot_packed_byte_array_ptr(&buf), 32) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_sha256_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha256)) == 0); + godot_string_destroy(&t); + + buf = godot_string_md5_buffer(&s); + CHECK(memcmp(md5_buf, godot_packed_byte_array_ptr(&buf), 16) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_md5_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&md5)) == 0); + godot_string_destroy(&t); + + godot_string_destroy(&s); + godot_string_destroy(&sha1); + godot_string_destroy(&sha256); + godot_string_destroy(&md5); +} + +TEST_CASE("[GDNatvie String] Join") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, ", "); + + godot_packed_string_array parts; + godot_packed_string_array_new(&parts); + godot_string_new_with_latin1_chars(&t, "One"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "B"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "C"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&u, "One, B, C"); + t = godot_string_join(&s, &parts); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); + godot_packed_string_array_destroy(&parts); +} + +TEST_CASE("[GDNatvie String] Is_*") { + static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" }; + static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false }; + static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false }; + static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false }; + static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false }; + static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false }; + static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false }; + + for (int i = 0; i < 12; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, data[i]); + CHECK(godot_string_is_numeric(&s) == isnum[i]); + CHECK(godot_string_is_valid_integer(&s) == isint[i]); + CHECK(godot_string_is_valid_hex_number(&s, false) == ishex[i]); + CHECK(godot_string_is_valid_hex_number(&s, true) == ishex_p[i]); + CHECK(godot_string_is_valid_float(&s) == isflt[i]); + CHECK(godot_string_is_valid_identifier(&s) == isid[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNatvie String] humanize_size") { + godot_string s; + + s = godot_string_humanize_size(1000); + CHECK(u32scmp(godot_string_get_data(&s), U"1000 B") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(1025); + CHECK(u32scmp(godot_string_get_data(&s), U"1.00 KiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(1025300); + CHECK(u32scmp(godot_string_get_data(&s), U"1001.2 KiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(100523550); + CHECK(u32scmp(godot_string_get_data(&s), U"95.86 MiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(5345555000); + CHECK(u32scmp(godot_string_get_data(&s), U"4.97 GiB") == 0); + godot_string_destroy(&s); +} + +#endif + +} // namespace TestGDNativeString + +#endif // TEST_GDNATVIE_STRING_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 4e172caadb..d41581883a 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -37,6 +37,7 @@ #include "test_class_db.h" #include "test_color.h" #include "test_expression.h" +#include "test_gdnative_string.h" #include "test_gradient.h" #include "test_gui.h" #include "test_math.h" diff --git a/tests/test_math.cpp b/tests/test_math.cpp index 84a85be2f6..862535b57e 100644 --- a/tests/test_math.cpp +++ b/tests/test_math.cpp @@ -162,7 +162,7 @@ class GetClassAndNamespace { } break; case '\'': case '"': { - CharType begin_str = code[idx]; + char32_t begin_str = code[idx]; idx++; String tk_string = String(); while (true) { @@ -176,13 +176,13 @@ class GetClassAndNamespace { } else if (code[idx] == '\\') { //escaped characters... idx++; - CharType next = code[idx]; + char32_t next = code[idx]; if (next == 0) { error_str = "Unterminated String"; error = true; return TK_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -241,7 +241,7 @@ class GetClassAndNamespace { if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { //a number - const CharType *rptr; + const char32_t *rptr; double number = String::to_float(&code[idx], &rptr); idx += (rptr - &code[idx]); value = number; diff --git a/tests/test_shader_lang.cpp b/tests/test_shader_lang.cpp index 34ee3e3210..d363ee22b5 100644 --- a/tests/test_shader_lang.cpp +++ b/tests/test_shader_lang.cpp @@ -325,7 +325,7 @@ MainLoop *test() { String code; while (true) { - CharType c = fa->get_8(); + char32_t c = fa->get_8(); if (fa->eof_reached()) { break; } diff --git a/tests/test_string.h b/tests/test_string.h index 22019a64c6..b041cb2f49 100644 --- a/tests/test_string.h +++ b/tests/test_string.h @@ -48,36 +48,159 @@ namespace TestString { -TEST_CASE("[String] Assign from cstr") { - String s = "Hello"; - CHECK(wcscmp(s.c_str(), L"Hello") == 0); +int u32scmp(const char32_t *l, const char32_t *r) { + for (; *l == *r && *l && *r; l++, r++) + ; + return *l - *r; } -TEST_CASE("[String] Assign from string (operator=)") { +TEST_CASE("[String] Assign Latin-1 char string") { + String s = "Hello"; + CHECK(u32scmp(s.get_data(), U"Hello") == 0); +} + +TEST_CASE("[String] Assign from Latin-1 char string (operator=)") { String s = "Dolly"; const String &t = s; - CHECK(wcscmp(t.c_str(), L"Dolly") == 0); + CHECK(u32scmp(t.get_data(), U"Dolly") == 0); } -TEST_CASE("[String] Assign from c-string (copycon)") { +TEST_CASE("[String] Assign from Latin-1 char string (copycon)") { String s("Sheep"); - const String &t(s); - CHECK(wcscmp(t.c_str(), L"Sheep") == 0); + const String &t1(s); + CHECK(u32scmp(t1.get_data(), U"Sheep") == 0); + + String t2 = String("Sheep", 3); + CHECK(u32scmp(t2.get_data(), U"She") == 0); } -TEST_CASE("[String] Assign from c-widechar (operator=)") { - String s(L"Give me"); - CHECK(wcscmp(s.c_str(), L"Give me") == 0); +TEST_CASE("[String] Assign from wchar_t string (operator=)") { + String s = L"Give me"; + CHECK(u32scmp(s.get_data(), U"Give me") == 0); } -TEST_CASE("[String] Assign from c-widechar (copycon)") { +TEST_CASE("[String] Assign from wchar_t string (copycon)") { String s(L"Wool"); - CHECK(wcscmp(s.c_str(), L"Wool") == 0); + CHECK(u32scmp(s.get_data(), U"Wool") == 0); +} + +TEST_CASE("[String] Assign from char32_t string (operator=)") { + String s = U"Give me"; + CHECK(u32scmp(s.get_data(), U"Give me") == 0); +} + +TEST_CASE("[String] Assign from char32_t string (copycon)") { + String s(U"Wool"); + CHECK(u32scmp(s.get_data(), U"Wool") == 0); +} + +TEST_CASE("[String] UTF8") { + /* how can i embed UTF in here? */ + static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + String s = u32str; + bool err = s.parse_utf8(s.utf8().get_data()); + CHECK(!err); + CHECK(s == u32str); + + err = s.parse_utf8((const char *)u8str); + CHECK(!err); + CHECK(s == u32str); + + CharString cs = (const char *)u8str; + CHECK(String::utf8(cs) == s); +} + +TEST_CASE("[String] UTF16") { + /* how can i embed UTF in here? */ + static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char16_t u16str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; + String s = u32str; + bool err = s.parse_utf16(s.utf16().get_data()); + CHECK(!err); + CHECK(s == u32str); + + err = s.parse_utf16(u16str); + CHECK(!err); + CHECK(s == u32str); + + Char16String cs = u16str; + CHECK(String::utf16(cs) == s); +} + +TEST_CASE("[String] UTF8 with BOM") { + /* how can i embed UTF in here? */ + static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + String s; + bool err = s.parse_utf8((const char *)u8str); + CHECK(!err); + CHECK(s == u32str); + + CharString cs = (const char *)u8str; + CHECK(String::utf8(cs) == s); +} + +TEST_CASE("[String] UTF16 with BOM") { + /* how can i embed UTF in here? */ + static const char32_t u32str[] = { 0x0020, 0x0045, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char16_t u16str[] = { 0xFEFF, 0x0020, 0x0045, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; + static const char16_t u16str_swap[] = { 0xFFFE, 0x2000, 0x4500, 0x4A30, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 }; + String s; + bool err = s.parse_utf16(u16str); + CHECK(!err); + CHECK(s == u32str); + + err = s.parse_utf16(u16str_swap); + CHECK(!err); + CHECK(s == u32str); + + Char16String cs = u16str; + CHECK(String::utf16(cs) == s); + + cs = u16str_swap; + CHECK(String::utf16(cs) == s); +} + +TEST_CASE("[String] Invalid UTF8") { + ERR_PRINT_OFF + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + String s; + bool err = s.parse_utf8((const char *)u8str); + CHECK(err); + CHECK(s == String()); + + CharString cs = (const char *)u8str; + CHECK(String::utf8(cs) == String()); + ERR_PRINT_ON +} + +TEST_CASE("[String] Invalid UTF16") { + ERR_PRINT_OFF + static const char16_t u16str[] = { 0x0045, 0x304A, 0x3088, 0x3046, 0xDFA4, 0 }; + String s; + bool err = s.parse_utf16(u16str); + CHECK(err); + CHECK(s == String()); + + Char16String cs = u16str; + CHECK(String::utf16(cs) == String()); + ERR_PRINT_ON +} + +TEST_CASE("[String] ASCII") { + String s = U"Primero Leche"; + String t = s.ascii(false).get_data(); + CHECK(s == t); + + t = s.ascii(true).get_data(); + CHECK(s == t); } TEST_CASE("[String] Comparisons (equal)") { String s = "Test Compare"; CHECK(s == "Test Compare"); + CHECK(s == U"Test Compare"); CHECK(s == L"Test Compare"); CHECK(s == String("Test Compare")); } @@ -85,6 +208,7 @@ TEST_CASE("[String] Comparisons (equal)") { TEST_CASE("[String] Comparisons (not equal)") { String s = "Test Compare"; CHECK(s != "Peanut"); + CHECK(s != U"Coconut"); CHECK(s != L"Coconut"); CHECK(s != String("Butter")); } @@ -92,6 +216,7 @@ TEST_CASE("[String] Comparisons (not equal)") { TEST_CASE("[String] Comparisons (operator <)") { String s = "Bees"; CHECK(s < "Elephant"); + CHECK(!(s < U"Amber")); CHECK(!(s < L"Amber")); CHECK(!(s < String("Beatrix"))); } @@ -103,7 +228,7 @@ TEST_CASE("[String] Concatenation") { s += ' '; s += 'a'; s += String(" "); - s = s + L"Nice"; + s = s + U"Nice"; s = s + " "; s = s + String("Day"); @@ -130,34 +255,49 @@ TEST_CASE("[String] Testing for empty string") { CHECK(String("").empty()); } +TEST_CASE("[String] Test chr") { + CHECK(String::chr('H') == "H"); + CHECK(String::chr(0x3012)[0] == 0x3012); + ERR_PRINT_OFF + CHECK(String::chr(0xd812)[0] == 0xfffd); // Unpaired UTF-16 surrogate + CHECK(String::chr(0x20d812)[0] == 0xfffd); // Outside UTF-32 range + ERR_PRINT_ON +} + TEST_CASE("[String] Operator []") { String a = "Kugar Sane"; a[0] = 'S'; a[6] = 'C'; CHECK(a == "Sugar Cane"); CHECK(a[1] == 'u'); + CHECK(a.ord_at(1) == 'u'); } TEST_CASE("[String] Case function test") { String a = "MoMoNgA"; CHECK(a.to_upper() == "MOMONGA"); + CHECK(a.to_lower() == "momonga"); +} + +TEST_CASE("[String] Case compare function test") { + String a = "MoMoNgA"; + + CHECK(a.casecmp_to("momonga") != 0); CHECK(a.nocasecmp_to("momonga") == 0); } -TEST_CASE("[String] UTF8") { - /* how can i embed UTF in here? */ - static const CharType ustr[] = { 0x304A, 0x360F, 0x3088, 0x3046, 0 }; - //static const wchar_t ustr[] = { 'P', 0xCE, 'p',0xD3, 0 }; - String s = ustr; - s.parse_utf8(s.utf8().get_data()); - CHECK(s == ustr); +TEST_CASE("[String] Natural compare function test") { + String a = "img2.png"; + + CHECK(a.nocasecmp_to("img10.png") > 0); + CHECK(a.naturalnocasecmp_to("img10.png") < 0); } -TEST_CASE("[String] ASCII") { - String s = L"Primero Leche"; - String t = s.ascii().get_data(); - CHECK(s == t); +TEST_CASE("[String] hex_encode_buffer") { + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 }; + String s = String::hex_encode_buffer(u8str, 6); + CHECK(s == U"45e3818a8fe3"); } TEST_CASE("[String] Substr") { @@ -165,24 +305,45 @@ TEST_CASE("[String] Substr") { CHECK(s.substr(3, 4) == "ler "); } -TEST_CASE("[string] Find") { - String s = "Pretty Woman"; - s.find("Revenge of the Monster Truck"); - +TEST_CASE("[String] Find") { + String s = "Pretty Woman Woman"; CHECK(s.find("tty") == 3); + CHECK(s.find("Wo", 9) == 13); CHECK(s.find("Revenge of the Monster Truck") == -1); + CHECK(s.rfind("man") == 15); } -TEST_CASE("[String] find no case") { - String s = "Pretty Whale"; +TEST_CASE("[String] Find no case") { + String s = "Pretty Whale Whale"; CHECK(s.findn("WHA") == 7); + CHECK(s.findn("WHA", 9) == 13); CHECK(s.findn("Revenge of the Monster SawFish") == -1); + CHECK(s.rfindn("WHA") == 13); +} + +TEST_CASE("[String] Find MK") { + Vector keys; + keys.push_back("sty"); + keys.push_back("tty"); + keys.push_back("man"); + + String s = "Pretty Woman"; + int key = 0; + + CHECK(s.findmk(keys, 0, &key) == 3); + CHECK(key == 1); + + CHECK(s.findmk(keys, 5, &key) == 9); + CHECK(key == 2); } TEST_CASE("[String] Find and replace") { String s = "Happy Birthday, Anna!"; s = s.replace("Birthday", "Halloween"); CHECK(s == "Happy Halloween, Anna!"); + + s = s.replace_first("H", "W"); + CHECK(s == "Wappy Halloween, Anna!"); } TEST_CASE("[String] Insertion") { @@ -193,6 +354,12 @@ TEST_CASE("[String] Insertion") { TEST_CASE("[String] Number to string") { CHECK(String::num(3.141593) == "3.141593"); + CHECK(String::num(3.141593, 3) == "3.142"); + CHECK(String::num_real(3.141593) == "3.141593"); + CHECK(String::num_scientific(30000000) == "3e+07"); + CHECK(String::num_int64(3141593) == "3141593"); + CHECK(String::num_int64(0xA141593, 16) == "a141593"); + CHECK(String::num_int64(0xA141593, 16, true) == "A141593"); } TEST_CASE("[String] String to integer") { @@ -204,6 +371,18 @@ TEST_CASE("[String] String to integer") { } } +TEST_CASE("[String] Hex to integer") { + static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" }; + static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD }; + static const bool wo_prefix[4] = { false, true, true, true }; + static const bool w_prefix[4] = { true, false, true, false }; + + for (int i = 0; i < 4; i++) { + CHECK((String(nums[i]).hex_to_int(true) == num[i]) == w_prefix[i]); + CHECK((String(nums[i]).hex_to_int(false) == num[i]) == wo_prefix[i]); + } +} + TEST_CASE("[String] String to float") { static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" }; static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 }; @@ -213,6 +392,11 @@ TEST_CASE("[String] String to float") { } } +TEST_CASE("[String] CamelCase to underscore") { + CHECK(String("TestTestStringGD").camelcase_to_underscore(false) == String("Test_Test_String_GD")); + CHECK(String("TestTestStringGD").camelcase_to_underscore(true) == String("test_test_string_gd")); +} + TEST_CASE("[String] Slicing") { String s = "Mars,Jupiter,Saturn,Uranus"; @@ -222,6 +406,69 @@ TEST_CASE("[String] Slicing") { } } +TEST_CASE("[String] Splitting") { + String s = "Mars,Jupiter,Saturn,Uranus"; + Vector l; + + const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" }; + const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" }; + + l = s.split(",", true, 2); + CHECK(l.size() == 3); + for (int i = 0; i < l.size(); i++) { + CHECK(l[i] == slices_l[i]); + } + + l = s.rsplit(",", true, 2); + CHECK(l.size() == 3); + for (int i = 0; i < l.size(); i++) { + CHECK(l[i] == slices_r[i]); + } + + s = "Mars Jupiter Saturn Uranus"; + const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; + l = s.split_spaces(); + for (int i = 0; i < l.size(); i++) { + CHECK(l[i] == slices_s[i]); + } + + s = "1.2;2.3 4.5"; + const double slices_d[3] = { 1.2, 2.3, 4.5 }; + + Vector f; + f = s.split_floats(";"); + CHECK(f.size() == 2); + for (int i = 0; i < f.size(); i++) { + CHECK(ABS(f[i] - slices_d[i]) <= 0.00001); + } + + Vector keys; + keys.push_back(";"); + keys.push_back(" "); + + f = s.split_floats_mk(keys); + CHECK(f.size() == 3); + for (int i = 0; i < f.size(); i++) { + CHECK(ABS(f[i] - slices_d[i]) <= 0.00001); + } + + s = "1;2 4"; + const int slices_i[3] = { 1, 2, 4 }; + + Vector ii; + ii = s.split_ints(";"); + CHECK(ii.size() == 2); + for (int i = 0; i < ii.size(); i++) { + CHECK(ii[i] == slices_i[i]); + } + + ii = s.split_ints_mk(keys); + CHECK(ii.size() == 3); + for (int i = 0; i < ii.size(); i++) { + CHECK(ii[i] == slices_i[i]); + } +} + TEST_CASE("[String] Erasing") { String s = "Josephine is such a cute girl!"; s.erase(s.find("cute "), String("cute ").length()); @@ -239,7 +486,7 @@ TEST_CASE("[String] Regex substitution") { struct test_27_data { char const *data; - char const *begin; + char const *part; bool expected; }; @@ -253,9 +500,9 @@ TEST_CASE("[String] Begins with") { bool state = true; for (size_t i = 0; state && i < count; ++i) { String s = tc[i].data; - state = s.begins_with(tc[i].begin) == tc[i].expected; + state = s.begins_with(tc[i].part) == tc[i].expected; if (state) { - String sb = tc[i].begin; + String sb = tc[i].part; state = s.begins_with(sb) == tc[i].expected; } CHECK(state); @@ -266,6 +513,42 @@ TEST_CASE("[String] Begins with") { CHECK(state); } +TEST_CASE("[String] Ends with") { + test_27_data tc[] = { + { "res://foobar", "foobar", true }, + { "res", "res://", false }, + { "abc", "abc", true } + }; + size_t count = sizeof(tc) / sizeof(tc[0]); + bool state = true; + for (size_t i = 0; state && i < count; ++i) { + String s = tc[i].data; + state = s.ends_with(tc[i].part) == tc[i].expected; + if (state) { + String sb = tc[i].part; + state = s.ends_with(sb) == tc[i].expected; + } + CHECK(state); + if (!state) { + break; + } + }; + CHECK(state); +} + +TEST_CASE("[String] format") { + const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; + + Dictionary value_dictionary; + value_dictionary["red"] = 10; + value_dictionary["green"] = 20; + value_dictionary["blue"] = "bla"; + value_dictionary["alpha"] = 0.4; + String value = value_format.format(value_dictionary, "$_"); + + CHECK(value == "red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\""); +} + TEST_CASE("[String] sprintf") { String format, output; Array args; @@ -560,6 +843,38 @@ TEST_CASE("[String] sprintf") { CHECK(output == "%c requires number or single-character string"); } +TEST_CASE("[String] is_numeric") { + CHECK(String("12").is_numeric()); + CHECK(String("1.2").is_numeric()); + CHECK(!String("AF").is_numeric()); + CHECK(String("-12").is_numeric()); + CHECK(String("-1.2").is_numeric()); +} + +TEST_CASE("[String] pad") { + String s = String("test"); + CHECK(s.lpad(10, "x") == U"xxxxxxtest"); + CHECK(s.rpad(10, "x") == U"testxxxxxx"); + + s = String("10.10"); + CHECK(s.pad_decimals(4) == U"10.1000"); + CHECK(s.pad_zeros(4) == U"0010.10"); +} + +TEST_CASE("[String] is_subsequence_of") { + String a = "is subsequence of"; + CHECK(String("sub").is_subsequence_of(a)); + CHECK(!String("Sub").is_subsequence_of(a)); + CHECK(String("Sub").is_subsequence_ofi(a)); +} + +TEST_CASE("[String] match") { + CHECK(String("img1.png").match("*.png")); + CHECK(!String("img1.jpeg").match("*.png")); + CHECK(!String("img1.Png").match("*.png")); + CHECK(String("img1.Png").matchn("*.png")); +} + TEST_CASE("[String] IPVX address to string") { IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, true); @@ -792,7 +1107,196 @@ TEST_CASE("[String] Count and countn functionality") { COUNT_TEST(String("testTest-TeStatest").countn("tEsT", 4, 16) == 2); CHECK(state); + +#undef COUNT_TEST } + +TEST_CASE("[String] Bigrams") { + String s = "abcd"; + Vector bigr = s.bigrams(); + + CHECK(bigr.size() == 3); + CHECK(bigr[0] == "ab"); + CHECK(bigr[1] == "bc"); + CHECK(bigr[2] == "cd"); +} + +TEST_CASE("[String] c-escape/unescape") { + String s = "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\""; + CHECK(s.c_escape().c_unescape() == s); +} + +TEST_CASE("[String] dedent") { + String s = " aaa\n bbb"; + String t = "aaa\nbbb"; + CHECK(s.dedent() == t); +} + +TEST_CASE("[String] Path functions") { + static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" }; + static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" }; + static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" }; + static const char *ext[4] = { "tscn", "xscn", "scn", "doc" }; + static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" }; + static const bool abs[4] = { true, true, false, false }; + + for (int i = 0; i < 4; i++) { + CHECK(String(path[i]).get_base_dir() == base_dir[i]); + CHECK(String(path[i]).get_basename() == base_name[i]); + CHECK(String(path[i]).get_extension() == ext[i]); + CHECK(String(path[i]).get_file() == file[i]); + CHECK(String(path[i]).is_abs_path() == abs[i]); + CHECK(String(path[i]).is_rel_path() != abs[i]); + CHECK(String(path[i]).simplify_path().get_base_dir().plus_file(file[i]) == String(path[i]).simplify_path()); + } + + static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" }; + static const bool valid[3] = { true, false, false }; + for (int i = 0; i < 3; i++) { + CHECK(String(file_name[i]).is_valid_filename() == valid[i]); + } +} + +TEST_CASE("[String] hash") { + String a = "Test"; + String b = "Test"; + String c = "West"; + CHECK(a.hash() == b.hash()); + CHECK(a.hash() != c.hash()); + + CHECK(a.hash64() == b.hash64()); + CHECK(a.hash64() != c.hash64()); +} + +TEST_CASE("[String] http_escape/unescape") { + String s = "Godot Engine:'docs'"; + String t = "Godot%20Engine%3A%27docs%27"; + + CHECK(s.http_escape() == t); + CHECK(t.http_unescape() == s); +} + +TEST_CASE("[String] percent_encode/decode") { // Note: is it redundant? Seems to be same as http_escape/unescape but in lower case. + String s = "Godot Engine:'docs'"; + String t = "Godot%20Engine%3a%27docs%27"; + + CHECK(s.percent_encode() == t); + CHECK(t.percent_decode() == s); +} + +TEST_CASE("[String] xml_escape/unescape") { + String s = "\"Test\" "; + CHECK(s.xml_escape(true).xml_unescape() == s); + CHECK(s.xml_escape(false).xml_unescape() == s); +} + +TEST_CASE("[String] Strip escapes") { + String s = "\t\tTest Test\r\n Test"; + CHECK(s.strip_escapes() == "Test Test Test"); +} + +TEST_CASE("[String] Similarity") { + String a = "Test"; + String b = "West"; + String c = "Toad"; + CHECK(a.similarity(b) > a.similarity(c)); +} + +TEST_CASE("[String] Strip edges") { + String s = "\t Test Test "; + CHECK(s.strip_edges(true, false) == "Test Test "); + CHECK(s.strip_edges(false, true) == "\t Test Test"); + CHECK(s.strip_edges(true, true) == "Test Test"); +} + +TEST_CASE("[String] Trim") { + String s = "aaaTestbbb"; + CHECK(s.trim_prefix("aaa") == "Testbbb"); + CHECK(s.trim_suffix("bbb") == "aaaTest"); + CHECK(s.trim_suffix("Test") == s); +} + +TEST_CASE("[String] Right/Left") { + String s = "aaaTestbbb"; + // ^ + CHECK(s.right(6) == "tbbb"); + CHECK(s.left(6) == "aaaTes"); +} + +TEST_CASE("[String] Repeat") { + String s = "abababab"; + String x = "ab"; + String t = x.repeat(4); + CHECK(t == s); +} + +TEST_CASE("[String] SHA1/SHA256/MD5") { + String s = "Godot"; + String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201"; + static uint8_t sha1_buf[20] = { + 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3, + 0x9D, 0xC2, 0xD2, 0x01 + }; + String sha256 = "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8"; + static uint8_t sha256_buf[32] = { + 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF, + 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8 + }; + String md5 = "4a336d087aeb0390da10ee2ea7cb87f8"; + static uint8_t md5_buf[16] = { + 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8 + }; + + PackedByteArray buf = s.sha1_buffer(); + CHECK(memcmp(sha1_buf, buf.ptr(), 20) == 0); + CHECK(s.sha1_text() == sha1); + + buf = s.sha256_buffer(); + CHECK(memcmp(sha256_buf, buf.ptr(), 32) == 0); + CHECK(s.sha256_text() == sha256); + + buf = s.md5_buffer(); + CHECK(memcmp(md5_buf, buf.ptr(), 16) == 0); + CHECK(s.md5_text() == md5); +} + +TEST_CASE("[String] Join") { + String s = ", "; + Vector parts; + parts.push_back("One"); + parts.push_back("B"); + parts.push_back("C"); + String t = s.join(parts); + CHECK(t == "One, B, C"); +} + +TEST_CASE("[String] Is_*") { + static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" }; + static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false }; + static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false }; + static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false }; + static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false }; + static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false }; + static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false }; + for (int i = 0; i < 12; i++) { + String s = String(data[i]); + CHECK(s.is_numeric() == isnum[i]); + CHECK(s.is_valid_integer() == isint[i]); + CHECK(s.is_valid_hex_number(false) == ishex[i]); + CHECK(s.is_valid_hex_number(true) == ishex_p[i]); + CHECK(s.is_valid_float() == isflt[i]); + CHECK(s.is_valid_identifier() == isid[i]); + } +} + +TEST_CASE("[String] humanize_size") { + CHECK(String::humanize_size(1000) == "1000 B"); + CHECK(String::humanize_size(1025) == "1.00 KiB"); + CHECK(String::humanize_size(1025300) == "1001.2 KiB"); + CHECK(String::humanize_size(100523550) == "95.86 MiB"); + CHECK(String::humanize_size(5345555000) == "4.97 GiB"); +} + } // namespace TestString #endif // TEST_STRING_H