Merge branch 'master' of https://github.com/FlaxEngine/FlaxEngine into double-vectors

This commit is contained in:
Jean-Baptiste Perrier
2021-08-18 12:50:11 +02:00
65 changed files with 19183 additions and 198 deletions

View File

@@ -296,7 +296,7 @@ namespace FlaxEditor.Content.Settings
}
// Create new settings asset and link it to the game settings
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, "Settings", CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
if (Editor.SaveJsonAsset(path, obj))
return true;
asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path);

View File

@@ -226,7 +226,9 @@ bool String::IsANSI() const
bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) const
{
if (prefix.IsEmpty() || prefix.Length() > Length())
if (prefix.IsEmpty())
return true;
if (prefix.Length() > Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return !StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length());
@@ -235,7 +237,9 @@ bool String::StartsWith(const StringView& prefix, StringSearchCase searchCase) c
bool String::EndsWith(const StringView& suffix, StringSearchCase searchCase) const
{
if (suffix.IsEmpty() || suffix.Length() > Length())
if (suffix.IsEmpty())
return true;
if (suffix.Length() > Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return !StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix);

View File

@@ -65,10 +65,11 @@ public:
/// <summary>
/// Lexicographically tests how this string compares to the other given string.
/// In case sensitive mode 'A' is less than 'a'.
/// </summary>
/// <param name="str">The another string test against.</param>
/// <param name="searchCase">The case sensitivity mode.</param>
/// <returns>0 if equal, -1 if less than, 1 if greater than.</returns>
/// <returns>0 if equal, negative number if less than, positive number if greater than.</returns>
int32 Compare(const StringBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
if (searchCase == StringSearchCase::CaseSensitive)
@@ -352,7 +353,7 @@ public:
bool StartsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 length = Length();
if (searchCase == StringSearchCase::IgnoreCase)
if (searchCase == StringSearchCase::CaseSensitive)
return length > 0 && _data[0] == c;
return length > 0 && StringUtils::ToLower(_data[0]) == StringUtils::ToLower(c);
}
@@ -360,14 +361,16 @@ public:
bool EndsWith(T c, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 length = Length();
if (searchCase == StringSearchCase::IgnoreCase)
if (searchCase == StringSearchCase::CaseSensitive)
return length > 0 && _data[length - 1] == c;
return length > 0 && StringUtils::ToLower(_data[length - 1]) == StringUtils::ToLower(c);
}
bool StartsWith(const StringBase& prefix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
if (prefix.IsEmpty() || Length() < prefix.Length())
if (prefix.IsEmpty())
return true;
if (Length() < prefix.Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return StringUtils::CompareIgnoreCase(this->GetText(), *prefix, prefix.Length()) == 0;
@@ -376,7 +379,9 @@ public:
bool EndsWith(const StringBase& suffix, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
if (suffix.IsEmpty() || Length() < suffix.Length())
if (suffix.IsEmpty())
return true;
if (Length() < suffix.Length())
return false;
if (searchCase == StringSearchCase::IgnoreCase)
return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0;
@@ -413,67 +418,94 @@ public:
return replacedChars;
}
/// <summary>
/// Replaces all occurences of searchText within current string with replacementText.
/// </summary>
/// <param name="searchText">String to search for. If empty or null no replacements are done.</param>
/// <param name="replacementText">String to replace with. Null is treated as empty string.</param>
/// <returns>Number of replacements made. (In case-sensitive mode if search text and replacement text are equal no replacements are done, and zero is returned.)</returns>
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);
}
/// <summary>
/// Replaces all occurences of searchText within current string with replacementText.
/// </summary>
/// <param name="searchText">String to search for.</param>
/// <param name="searchTextLength">Length of searchText. Must be greater than zero.</param>
/// <param name="replacementText">String to replace with. Null is treated as empty string.</param>
/// <param name="replacementTextLength">Length of replacementText.</param>
/// <returns>Number of replacements made (in other words number of occurences of searchText).</returns>
int32 Replace(const T* searchText, int32 searchTextLength, const T* replacementText, int32 replacementTextLength, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
{
if (!HasChars())
return 0;
if (searchTextLength == 0)
{
const int32 searchTextLength = StringUtils::Length(searchText);
const int32 replacementTextLength = StringUtils::Length(replacementText);
if (searchTextLength == replacementTextLength)
ASSERT(false); // Empty search text never makes sense, and is always sign of a bug in calling code.
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;

View File

@@ -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;
}

View File

@@ -54,18 +54,23 @@ public:
/// <summary>
/// Lexicographically tests how this string compares to the other given string.
/// In case sensitive mode 'A' is less than 'a'.
/// </summary>
/// <param name="str">The another string test against.</param>
/// <param name="searchCase">The case sensitivity mode.</param>
/// <returns>0 if equal, -1 if less than, 1 if greater than.</returns>
/// <returns>0 if equal, negative number if less than, positive number if greater than.</returns>
int32 Compare(const StringViewBase& str, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 lengthDiff = Length() - str.Length();
if (lengthDiff != 0)
return lengthDiff;
if (searchCase == StringSearchCase::CaseSensitive)
return StringUtils::Compare(this->GetText(), str.GetText(), Length());
return StringUtils::CompareIgnoreCase(this->GetText(), str.GetText(), Length());
const bool thisIsShorter = Length() < str.Length();
const int32 minLength = thisIsShorter ? Length() : str.Length();
const int32 prefixCompare = (searchCase == StringSearchCase::CaseSensitive)
? StringUtils::Compare(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength)
: StringUtils::CompareIgnoreCase(this->GetNonTerminatedText(), str.GetNonTerminatedText(), minLength);
if (prefixCompare != 0)
return prefixCompare;
if (Length() == str.Length())
return 0;
return thisIsShorter ? -1 : 1;
}
public:
@@ -95,7 +100,7 @@ public:
}
/// <summary>
/// Gets the pointer to the string.
/// Gets the pointer to the string. Pointer can be null, and won't be null-terminated.
/// </summary>
FORCE_INLINE constexpr const T* operator*() const
{
@@ -103,7 +108,7 @@ public:
}
/// <summary>
/// Gets the pointer to the string.
/// Gets the pointer to the string. Pointer can be null, and won't be null-terminated.
/// </summary>
FORCE_INLINE constexpr const T* Get() const
{
@@ -111,9 +116,9 @@ public:
}
/// <summary>
/// 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.
/// </summary>
FORCE_INLINE const T* GetText() const
FORCE_INLINE const T* GetNonTerminatedText() const
{
return _data ? _data : (const T*)TEXT("");
}
@@ -177,15 +182,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 +239,7 @@ public:
/// <summary>
/// Initializes a new instance of the <see cref="StringView"/> class.
/// </summary>
/// <param name="str">The characters sequence.</param>
/// <param name="str">The characters sequence. If null, constructed StringView will be empty.</param>
StringView(const Char* str)
{
_data = str;
@@ -242,7 +249,7 @@ public:
/// <summary>
/// Initializes a new instance of the <see cref="StringView"/> class.
/// </summary>
/// <param name="str">The characters sequence.</param>
/// <param name="str">The characters sequence. Can be null if length is zero.</param>
/// <param name="length">The characters sequence length (excluding null-terminator character).</param>
constexpr StringView(const Char* str, int32 length)
: StringViewBase<Char>(str, length)
@@ -270,7 +277,7 @@ public:
/// <returns>True if this string is lexicographically equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator==(const StringView& other) const
{
return StringUtils::Compare(this->GetText(), other.GetText()) == 0;
return this->Compare(other) == 0;
}
/// <summary>
@@ -280,7 +287,7 @@ public:
/// <returns>True if this string is lexicographically is not equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator!=(const StringView& other) const
{
return StringUtils::Compare(this->GetText(), other.GetText()) != 0;
return this->Compare(other) != 0;
}
/// <summary>
@@ -290,7 +297,7 @@ public:
/// <returns>True if this string is lexicographically equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator==(const Char* other) const
{
return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) == 0;
return this->Compare(StringView(other)) == 0;
}
/// <summary>
@@ -300,7 +307,7 @@ public:
/// <returns>True if this string is lexicographically is not equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator!=(const Char* other) const
{
return StringUtils::Compare(this->GetText(), other ? other : TEXT("")) != 0;
return this->Compare(StringView(other)) != 0;
}
/// <summary>
@@ -459,7 +466,7 @@ public:
/// <returns>True if this string is lexicographically equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator==(const StringAnsiView& other) const
{
return StringUtils::Compare(this->GetText(), other.GetText()) == 0;
return this->Compare(other) == 0;
}
/// <summary>
@@ -469,7 +476,7 @@ public:
/// <returns>True if this string is lexicographically is not equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator!=(const StringAnsiView& other) const
{
return StringUtils::Compare(this->GetText(), other.GetText()) != 0;
return this->Compare(other) != 0;
}
/// <summary>
@@ -479,7 +486,7 @@ public:
/// <returns>True if this string is lexicographically equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator==(const char* other) const
{
return StringUtils::Compare(this->GetText(), other ? other : "") == 0;
return this->Compare(StringAnsiView(other)) == 0;
}
/// <summary>
@@ -489,7 +496,7 @@ public:
/// <returns>True if this string is lexicographically is not equivalent to the other, otherwise false.</returns>
FORCE_INLINE bool operator!=(const char* other) const
{
return StringUtils::Compare(this->GetText(), other ? other : "") != 0;
return this->Compare(StringAnsiView(other)) != 0;
}
/// <summary>

View File

@@ -270,8 +270,8 @@ public:
explicit operator float() const;
explicit operator double() const;
explicit operator void*() const;
explicit operator StringView() const;
explicit operator StringAnsiView() const;
explicit operator StringView() const; // Returned StringView, if not empty, is guaranteed to point to a null terminated buffer.
explicit operator StringAnsiView() const; // Returned StringView, if not empty, is guaranteed to point to a null terminated buffer.
explicit operator ScriptingObject*() const;
explicit operator struct _MonoObject*() const;
explicit operator Asset*() const;