From aecc81f5e5adde21980c5cbed4a243d14f7646ce Mon Sep 17 00:00:00 2001 From: Zbigniew Skowron Date: Sun, 8 Aug 2021 22:04:54 +0200 Subject: [PATCH] Fixed many invalid uses of StringView::GetText(), where a null-terminated string was required. Renamed GetText() to GetNonTerminatedText() to reduce chance of same bugs appearing in the future. --- Source/Editor/Utilities/EditorUtilities.cpp | 2 +- Source/Engine/Content/Content.cpp | 9 +- Source/Engine/Content/Content.h | 8 ++ Source/Engine/Core/Types/String.h | 132 +++++++++++------- Source/Engine/Core/Types/StringView.cpp | 16 +-- Source/Engine/Core/Types/StringView.h | 41 +++--- Source/Engine/Localization/Localization.cpp | 10 +- Source/Engine/Platform/Base/PlatformBase.h | 12 +- .../Engine/Platform/Linux/LinuxPlatform.cpp | 2 +- Source/Engine/Platform/StringUtils.h | 20 +-- Source/Engine/Platform/UWP/UWPPlatform.cpp | 2 +- .../Platform/Windows/WindowsClipboard.cpp | 2 +- .../Platform/Windows/WindowsPlatform.cpp | 8 +- .../Bindings/BindingsGenerator.Cpp.cs | 2 +- 14 files changed, 158 insertions(+), 108 deletions(-) diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index 85d409b92..1b271d3d1 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -816,6 +816,6 @@ bool EditorUtilities::ReplaceInFile(const StringView& file, const StringView& fi String text; if (File::ReadAllText(file, text)) return true; - text.Replace(findWhat.GetText(), replaceWith.GetText()); + text.Replace(findWhat.Get(), findWhat.Length(), replaceWith.Get(), replaceWith.Length()); return File::WriteAllText(file, text, Encoding::ANSI); } diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 98e2c241c..abe40e285 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -384,12 +384,12 @@ Asset* Content::LoadAsyncInternal(const StringView& internalPath, MClass* type) CHECK_RETURN(type, nullptr); const auto scriptingType = Scripting::FindScriptingType(type->GetFullName()); if (scriptingType) - return LoadAsyncInternal(internalPath.GetText(), scriptingType); + return LoadAsyncInternal(internalPath, scriptingType); LOG(Error, "Failed to find asset type '{0}'.", String(type->GetFullName())); return nullptr; } -Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeHandle& type) +Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type) { #if USE_EDITOR const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT; @@ -411,6 +411,11 @@ Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeH return asset; } +Asset* Content::LoadAsyncInternal(const Char* internalPath, const ScriptingTypeHandle& type) +{ + return LoadAsyncInternal(StringView(internalPath), type); +} + FLAXENGINE_API Asset* LoadAsset(const Guid& id, const ScriptingTypeHandle& type) { return Content::LoadAsync(id, type); diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h index da558dc5f..c8e3cad90 100644 --- a/Source/Engine/Content/Content.h +++ b/Source/Engine/Content/Content.h @@ -187,6 +187,14 @@ public: /// The loaded asset or null if failed. API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, MClass* type); + /// + /// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. + /// + /// The path of the asset relative to the engine internal content (excluding the extension). + /// The asset type. If loaded object has different type (excluding types derived from the given) the loading fails. + /// The loaded asset or null if failed. + static Asset* LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type); + /// /// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. /// diff --git a/Source/Engine/Core/Types/String.h b/Source/Engine/Core/Types/String.h index 6871cb287..0a62c516f 100644 --- a/Source/Engine/Core/Types/String.h +++ b/Source/Engine/Core/Types/String.h @@ -413,67 +413,97 @@ public: return replacedChars; } + /// + /// Replaces all occurences of searchText within current string with replacementText. + /// + /// String to search for. If empty or null no replacements are done. + /// String to replace with. Null is treated as empty string. + /// Number of replacements made. (In case-sensitive mode if search text and replacement text are equal no replacements are done, and zero is returned.) int32 Replace(const T* searchText, const T* replacementText, StringSearchCase searchCase = StringSearchCase::CaseSensitive) { - int32 replacedCount = 0; - if (HasChars() && searchText && *searchText && replacementText && (searchCase == StringSearchCase::IgnoreCase || StringUtils::Compare(searchText, replacementText) != 0)) + const int32 searchTextLength = StringUtils::Length(searchText); + const int32 replacementTextLength = StringUtils::Length(replacementText); + return Replace(searchText, searchTextLength, replacementText, replacementTextLength, searchCase); + } + + /// + /// Replaces all occurences of searchText within current string with replacementText. + /// + /// String to search for. If empty or null no replacements are done. + /// Length of searchText. + /// String to replace with. Null is treated as empty string. + /// Length of replacementText. + /// Number of replacements made. (In case-sensitive mode if search text and replacement text are equal no replacements are done, and zero is returned.) + int32 Replace(const T* searchText, int32 searchTextLength, const T* replacementText, int32 replacementTextLength, StringSearchCase searchCase = StringSearchCase::CaseSensitive) + { + if (!HasChars()) + return 0; + + if (searchTextLength == 0) + return 0; + + // If we are doing case sensitive search and replacement text is equal to search text, we do nothing. + if ((searchCase == StringSearchCase::CaseSensitive) && (searchTextLength == replacementTextLength) && (StringUtils::Compare(searchText, replacementText) == 0)) { - const int32 searchTextLength = StringUtils::Length(searchText); - const int32 replacementTextLength = StringUtils::Length(replacementText); - if (searchTextLength == replacementTextLength) + return 0; + } + + int32 replacedCount = 0; + + if (searchTextLength == replacementTextLength) + { + T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText)); + while (pos != nullptr) { - T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText)); - while (pos != nullptr) - { - replacedCount++; + replacedCount++; - for (int32 i = 0; i < replacementTextLength; i++) - pos[i] = replacementText[i]; + for (int32 i = 0; i < replacementTextLength; i++) + pos[i] = replacementText[i]; - if (pos + searchTextLength - **this < Length()) - pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + searchTextLength, searchText) : StringUtils::Find(pos + searchTextLength, searchText)); - else - break; - } + if (pos + searchTextLength - **this < Length()) + pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + searchTextLength, searchText) : StringUtils::Find(pos + searchTextLength, searchText)); + else + break; } - else if (Contains(searchText, searchCase)) + } + else if (Contains(searchText, searchCase)) + { + T* readPosition = _data; + T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); + while (searchPosition != nullptr) { - T* readPosition = _data; - T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); - while (searchPosition != nullptr) - { - replacedCount++; - readPosition = searchPosition + searchTextLength; - searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); - } - - const auto oldLength = _length; - const auto oldData = _data; - _length += replacedCount * (replacementTextLength - searchTextLength); - _data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16); - - T* writePosition = _data; - readPosition = oldData; + replacedCount++; + readPosition = searchPosition + searchTextLength; searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); - while (searchPosition != nullptr) - { - const int32 writeOffset = (int32)(searchPosition - readPosition); - Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T)); - writePosition += writeOffset; - - Platform::MemoryCopy(writePosition, replacementText, replacementTextLength * sizeof(T)); - writePosition += replacementTextLength; - - readPosition = searchPosition + searchTextLength; - searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); - } - - const int32 writeOffset = (int32)(oldData - readPosition) + oldLength; - Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T)); - - _data[_length] = 0; - Platform::Free(oldData); } + + const auto oldLength = _length; + const auto oldData = _data; + _length += replacedCount * (replacementTextLength - searchTextLength); + _data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16); + + T* writePosition = _data; + readPosition = oldData; + searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); + while (searchPosition != nullptr) + { + const int32 writeOffset = (int32)(searchPosition - readPosition); + Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T)); + writePosition += writeOffset; + + if (replacementTextLength > 0) + Platform::MemoryCopy(writePosition, replacementText, replacementTextLength * sizeof(T)); + writePosition += replacementTextLength; + + readPosition = searchPosition + searchTextLength; + searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText)); + } + + const int32 writeOffset = (int32)(oldData - readPosition) + oldLength; + Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T)); + + _data[_length] = 0; + Platform::Free(oldData); } return replacedCount; diff --git a/Source/Engine/Core/Types/StringView.cpp b/Source/Engine/Core/Types/StringView.cpp index 606e37ad1..4df8bc8e9 100644 --- a/Source/Engine/Core/Types/StringView.cpp +++ b/Source/Engine/Core/Types/StringView.cpp @@ -18,12 +18,12 @@ StringView::StringView(const String& str) bool StringView::operator==(const String& other) const { - return StringUtils::Compare(this->GetText(), *other) == 0; + return this->Compare(StringView(other)) == 0; } bool StringView::operator!=(const String& other) const { - return StringUtils::Compare(this->GetText(), *other) != 0; + return this->Compare(StringView(other)) != 0; } StringView StringView::Left(int32 count) const @@ -62,12 +62,12 @@ StringAnsi StringView::ToStringAnsi() const bool operator==(const String& a, const StringView& b) { - return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) == 0; + return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) == 0; } bool operator!=(const String& a, const StringView& b) { - return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) != 0; + return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) != 0; } StringAnsiView StringAnsiView::Empty; @@ -79,12 +79,12 @@ StringAnsiView::StringAnsiView(const StringAnsi& str) bool StringAnsiView::operator==(const StringAnsi& other) const { - return StringUtils::Compare(this->GetText(), *other) == 0; + return this->Compare(StringAnsiView(other)) == 0; } bool StringAnsiView::operator!=(const StringAnsi& other) const { - return StringUtils::Compare(this->GetText(), *other) != 0; + return this->Compare(StringAnsiView(other)) != 0; } StringAnsi StringAnsiView::Substring(int32 startIndex) const @@ -111,10 +111,10 @@ StringAnsi StringAnsiView::ToStringAnsi() const bool operator==(const StringAnsi& a, const StringAnsiView& b) { - return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) == 0; + return a.Length() == b.Length() && StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) == 0; } bool operator!=(const StringAnsi& a, const StringAnsiView& b) { - return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetText(), b.Length()) != 0; + return a.Length() != b.Length() || StringUtils::Compare(a.GetText(), b.GetNonTerminatedText(), b.Length()) != 0; } diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index 0ed2580ce..f0d608493 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -63,9 +63,12 @@ public: const int32 lengthDiff = Length() - str.Length(); if (lengthDiff != 0) return lengthDiff; + if (Length() == 0) + return 0; + // We know here that both this StringView and str are not empty, and therefore Get() below are valid. if (searchCase == StringSearchCase::CaseSensitive) - return StringUtils::Compare(this->GetText(), str.GetText(), Length()); - return StringUtils::CompareIgnoreCase(this->GetText(), str.GetText(), Length()); + return StringUtils::Compare(this->Get(), str.Get(), Length()); + return StringUtils::CompareIgnoreCase(this->Get(), str.Get(), Length()); } public: @@ -95,7 +98,7 @@ public: } /// - /// Gets the pointer to the string. + /// Gets the pointer to the string. Pointer can be null, and won't be null-terminated. /// FORCE_INLINE constexpr const T* operator*() const { @@ -103,7 +106,7 @@ public: } /// - /// Gets the pointer to the string. + /// Gets the pointer to the string. Pointer can be null, and won't be null-terminated. /// FORCE_INLINE constexpr const T* Get() const { @@ -111,9 +114,9 @@ public: } /// - /// Gets the pointer to the string or to the static empty text if string is null. Returned pointer is always valid (read-only). + /// Gets the pointer to the string or to the static empty text if string is null. Returned pointer is always non-null, but is not null-terminated. /// - FORCE_INLINE const T* GetText() const + FORCE_INLINE const T* GetNonTerminatedText() const { return _data ? _data : (const T*)TEXT(""); } @@ -177,15 +180,17 @@ public: { if (prefix.IsEmpty() || Length() < prefix.Length()) return false; + // We know that this StringView is not empty, and therefore Get() below is valid. if (searchCase == StringSearchCase::IgnoreCase) - return StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length()) == 0; - return StringUtils::Compare(this->GetText(), *prefix, prefix.Length()) == 0; + return StringUtils::CompareIgnoreCase(this->Get(), *prefix, prefix.Length()) == 0; + return StringUtils::Compare(this->Get(), *prefix, prefix.Length()) == 0; } bool EndsWith(const StringViewBase& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const { if (suffix.IsEmpty() || Length() < suffix.Length()) return false; + // We know that this StringView is not empty, and therefore accessing data below is valid. if (searchCase == StringSearchCase::IgnoreCase) return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0; return StringUtils::Compare(&(*this)[Length() - suffix.Length()], *suffix) == 0; @@ -232,7 +237,7 @@ public: /// /// Initializes a new instance of the class. /// - /// The characters sequence. + /// The characters sequence. If null, constructed StringView will be empty. StringView(const Char* str) { _data = str; @@ -242,7 +247,7 @@ public: /// /// Initializes a new instance of the class. /// - /// The characters sequence. + /// The characters sequence. Can be null if length is zero. /// The characters sequence length (excluding null-terminator character). constexpr StringView(const Char* str, int32 length) : StringViewBase(str, length) @@ -270,7 +275,7 @@ public: /// True if this string is lexicographically equivalent to the other, otherwise false. FORCE_INLINE bool operator==(const StringView& other) const { - return StringUtils::Compare(this->GetText(), other.GetText()) == 0; + return this->Compare(other) == 0; } /// @@ -280,7 +285,7 @@ public: /// True if this string is lexicographically is not equivalent to the other, otherwise false. FORCE_INLINE bool operator!=(const StringView& other) const { - return StringUtils::Compare(this->GetText(), other.GetText()) != 0; + return this->Compare(other) != 0; } /// @@ -290,7 +295,7 @@ public: /// True if this string is lexicographically equivalent to the other, otherwise false. FORCE_INLINE bool operator==(const Char* other) const { - return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) == 0; + return this->Compare(StringView(other)) == 0; } /// @@ -300,7 +305,7 @@ public: /// True if this string is lexicographically is not equivalent to the other, otherwise false. FORCE_INLINE bool operator!=(const Char* other) const { - return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) != 0; + return this->Compare(StringView(other)) != 0; } /// @@ -459,7 +464,7 @@ public: /// True if this string is lexicographically equivalent to the other, otherwise false. FORCE_INLINE bool operator==(const StringAnsiView& other) const { - return StringUtils::Compare(this->GetText(), other.GetText()) == 0; + return this->Compare(other) == 0; } /// @@ -469,7 +474,7 @@ public: /// True if this string is lexicographically is not equivalent to the other, otherwise false. FORCE_INLINE bool operator!=(const StringAnsiView& other) const { - return StringUtils::Compare(this->GetText(), other.GetText()) != 0; + return this->Compare(other) != 0; } /// @@ -479,7 +484,7 @@ public: /// True if this string is lexicographically equivalent to the other, otherwise false. FORCE_INLINE bool operator==(const char* other) const { - return StringUtils::Compare(this->GetText(), other ? other : "") == 0; + return this->Compare(StringAnsiView(other)) == 0; } /// @@ -489,7 +494,7 @@ public: /// True if this string is lexicographically is not equivalent to the other, otherwise false. FORCE_INLINE bool operator!=(const char* other) const { - return StringUtils::Compare(this->GetText(), other ? other : "") != 0; + return this->Compare(StringAnsiView(other)) != 0; } /// diff --git a/Source/Engine/Localization/Localization.cpp b/Source/Engine/Localization/Localization.cpp index defab1c4f..7ac0a09ed 100644 --- a/Source/Engine/Localization/Localization.cpp +++ b/Source/Engine/Localization/Localization.cpp @@ -278,18 +278,18 @@ String Localization::GetPluralString(const String& id, int32 n, const String& fa { CHECK_RETURN(n >= 1, fallback); n--; - StringView result; + const String* result = nullptr; for (auto& e : Instance.LocalizedStringTables) { const auto table = e.Get(); const auto messages = table ? table->Entries.TryGet(id) : nullptr; if (messages && messages->Count() > n) { - result = messages->At(n); + result = &messages->At(n); break; } } - if (result.IsEmpty()) - result = fallback; - return String::Format(result.GetText(), n); + if (!result) + result = &fallback; + return String::Format(result->GetText(), n); } diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index b6d5d67bd..ac2150c58 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -169,8 +169,8 @@ public: /// /// Copy memory region /// - /// Destination memory address - /// Source memory address + /// Destination memory address. Must not be null, even if size is zero. + /// Source memory address. Must not be null, even if size is zero. /// Size of the memory to copy in bytes FORCE_INLINE static void MemoryCopy(void* dst, const void* src, uint64 size) { @@ -180,7 +180,7 @@ public: /// /// Set memory region with given value /// - /// Destination memory address + /// Destination memory address. Must not be null, even if size is zero. /// Size of the memory to set in bytes /// Value to set FORCE_INLINE static void MemorySet(void* dst, uint64 size, int32 value) @@ -191,7 +191,7 @@ public: /// /// Clear memory region with zeros /// - /// Destination memory address + /// Destination memory address. Must not be null, even if size is zero. /// Size of the memory to clear in bytes FORCE_INLINE static void MemoryClear(void* dst, uint64 size) { @@ -201,8 +201,8 @@ public: /// /// Compare two blocks of the memory. /// - /// The first buffer address. - /// The second buffer address. + /// The first buffer address. Must not be null, even if size is zero. + /// The second buffer address. Must not be null, even if size is zero. /// Size of the memory to compare in bytes. FORCE_INLINE static int32 MemoryCompare(const void* buf1, const void* buf2, uint64 size) { diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index f0339225a..49c4e3aaa 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -1659,7 +1659,7 @@ void LinuxClipboard::SetText(const StringView& text) return; X11::Window window = (X11::Window)mainWindow->GetNativePtr(); - Impl::ClipboardText.Set(text.GetText(), text.Length()); + Impl::ClipboardText.Set(text.Get(), text.Length()); X11::XSetSelectionOwner(xDisplay, xAtomClipboard, window, CurrentTime); // CLIPBOARD X11::XSetSelectionOwner(xDisplay, (X11::Atom)1, window, CurrentTime); // XA_PRIMARY } diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h index 101049b8e..4905ec646 100644 --- a/Source/Engine/Platform/StringUtils.h +++ b/Source/Engine/Platform/StringUtils.h @@ -120,36 +120,36 @@ public: public: - // Compare two strings with case sensitive + // Compare two strings with case sensitive. Strings must not be null. static int32 Compare(const Char* str1, const Char* str2); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 Compare(const Char* str1, const Char* str2, int32 maxCount); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 CompareIgnoreCase(const Char* str1, const Char* str2); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 CompareIgnoreCase(const Char* str1, const Char* str2, int32 maxCount); - // Compare two strings with case sensitive + // Compare two strings with case sensitive. Strings must not be null. static int32 Compare(const char* str1, const char* str2); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 Compare(const char* str1, const char* str2, int32 maxCount); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 CompareIgnoreCase(const char* str1, const char* str2); - // Compare two strings without case sensitive + // Compare two strings without case sensitive. Strings must not be null. static int32 CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount); public: - // Get string length + // Get string length. Returns 0 if str is null. static int32 Length(const Char* str); - // Get string length + // Get string length. Returns 0 if str is null. static int32 Length(const char* str); // Copy string diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp index b0fdf8f21..aeb799a2c 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.cpp +++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp @@ -35,7 +35,7 @@ void RunUWP() DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) { - return (DialogResult)CUWPPlatform->ShowMessageDialog(parent ? parent->GetImpl() : nullptr, text.GetText(), caption.GetText(), (UWPPlatformImpl::MessageBoxButtons)buttons, (UWPPlatformImpl::MessageBoxIcon)icon); + return (DialogResult)CUWPPlatform->ShowMessageDialog(parent ? parent->GetImpl() : nullptr, String(text).GetText(), String(caption).GetText(), (UWPPlatformImpl::MessageBoxButtons)buttons, (UWPPlatformImpl::MessageBoxIcon)icon); } bool UWPPlatform::Init() diff --git a/Source/Engine/Platform/Windows/WindowsClipboard.cpp b/Source/Engine/Platform/Windows/WindowsClipboard.cpp index 196c1d7b3..baba81e8f 100644 --- a/Source/Engine/Platform/Windows/WindowsClipboard.cpp +++ b/Source/Engine/Platform/Windows/WindowsClipboard.cpp @@ -25,7 +25,7 @@ void WindowsClipboard::SetText(const StringView& text) { const int32 size = (text.Length() + 1) * sizeof(Char); const HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, size); - Platform::MemoryCopy(GlobalLock(hMem), text.GetText(), size); + Platform::MemoryCopy(GlobalLock(hMem), String(text).GetText(), size); GlobalUnlock(hMem); OpenClipboard(nullptr); diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 759713f7c..6849c0ac8 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -435,7 +435,7 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri } // Show dialog - int result = MessageBoxW(parent ? static_cast(parent->GetNativePtr()) : nullptr, text.GetText(), caption.GetText(), flags); + int result = MessageBoxW(parent ? static_cast(parent->GetNativePtr()) : nullptr, String(text).GetText(), String(caption).GetText(), flags); // Translate result to dialog result DialogResult dialogResult; @@ -948,10 +948,12 @@ int32 WindowsPlatform::StartProcess(const StringView& filename, const StringView LOG(Info, "Working directory: {0}", workingDir); } + String filenameString(filename); + SHELLEXECUTEINFOW shExecInfo = { 0 }; shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - shExecInfo.lpFile = filename.GetText(); + shExecInfo.lpFile = filenameString.GetText(); shExecInfo.lpParameters = args.HasChars() ? args.Get() : nullptr; shExecInfo.lpDirectory = workingDir.HasChars() ? workingDir.Get() : nullptr; shExecInfo.nShow = hiddenWindow ? SW_HIDE : SW_SHOW; @@ -1109,7 +1111,7 @@ int32 WindowsPlatform::RunProcess(const StringView& cmdLine, const StringView& w // Create the process PROCESS_INFORMATION procInfo; - if (!CreateProcessW(nullptr, const_cast(cmdLine.GetText()), nullptr, nullptr, TRUE, dwCreationFlags, (LPVOID)environmentStr, workingDir.HasChars() ? workingDir.Get() : nullptr, &startupInfoEx.StartupInfo, &procInfo)) + if (!CreateProcessW(nullptr, const_cast(String(cmdLine).GetText()), nullptr, nullptr, TRUE, dwCreationFlags, (LPVOID)environmentStr, String(workingDir).GetText(), &startupInfoEx.StartupInfo, &procInfo)) { LOG(Warning, "Cannot start process '{0}'. Error code: 0x{1:x}", cmdLine, static_cast(GetLastError())); goto ERROR_EXIT; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index e61a2cf0a..a071e161c 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -166,7 +166,7 @@ namespace Flax.Build.Bindings if (typeInfo.Type == "String") return $"(StringView){value}"; if (typeInfo.IsPtr && typeInfo.IsConst && typeInfo.Type == "Char") - return $"((StringView){value}).GetText()"; + return $"((StringView){value}).GetNonTerminatedText()"; // This is a bug, as we need a null-terminated strig here. Any idea how to fix it? if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})"; if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")