diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 10:01:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 10:01:47 +0200 |
| commit | 075d17f8ada47e990fe94606c3d21df409223465 (patch) | |
| tree | e50549b766a2f3c354798a54ff73404217b4c9af /zencore/string.cpp | |
| parent | fix: bundle shouldn't append content zip to zen (diff) | |
| download | zen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz zen-075d17f8ada47e990fe94606c3d21df409223465.zip | |
moved source directories into `/src` (#264)
* moved source directories into `/src`
* updated bundle.lua for new `src` path
* moved some docs, icon
* removed old test trees
Diffstat (limited to 'zencore/string.cpp')
| -rw-r--r-- | zencore/string.cpp | 1004 |
1 files changed, 0 insertions, 1004 deletions
diff --git a/zencore/string.cpp b/zencore/string.cpp deleted file mode 100644 index ad6ee78fc..000000000 --- a/zencore/string.cpp +++ /dev/null @@ -1,1004 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include <zencore/memory.h> -#include <zencore/string.h> -#include <zencore/testing.h> - -#include <inttypes.h> -#include <math.h> -#include <stdio.h> -#include <exception> -#include <ostream> -#include <stdexcept> - -#include <utf8.h> - -template<typename u16bit_iterator> -void -utf16to8_impl(u16bit_iterator StartIt, u16bit_iterator EndIt, ::zen::StringBuilderBase& OutString) -{ - while (StartIt != EndIt) - { - uint32_t cp = utf8::internal::mask16(*StartIt++); - // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) - { - uint32_t trail_surrogate = utf8::internal::mask16(*StartIt++); - cp = (cp << 10) + trail_surrogate + utf8::internal::SURROGATE_OFFSET; - } - OutString.AppendCodepoint(cp); - } -} - -template<typename u32bit_iterator> -void -utf32to8_impl(u32bit_iterator StartIt, u32bit_iterator EndIt, ::zen::StringBuilderBase& OutString) -{ - for (; StartIt != EndIt; ++StartIt) - { - wchar_t cp = *StartIt; - OutString.AppendCodepoint(cp); - } -} - -////////////////////////////////////////////////////////////////////////// - -namespace zen { - -bool -ToString(std::span<char> Buffer, uint64_t Num) -{ - snprintf(Buffer.data(), Buffer.size(), "%" PRIu64, Num); - - return true; -} -bool -ToString(std::span<char> Buffer, int64_t Num) -{ - snprintf(Buffer.data(), Buffer.size(), "%" PRId64, Num); - - return true; -} - -////////////////////////////////////////////////////////////////////////// - -const char* -FilepathFindExtension(const std::string_view& Path, const char* ExtensionToMatch) -{ - const size_t PathLen = Path.size(); - - if (ExtensionToMatch) - { - size_t ExtLen = strlen(ExtensionToMatch); - - if (ExtLen > PathLen) - return nullptr; - - const char* PathExtension = Path.data() + PathLen - ExtLen; - - if (StringEquals(PathExtension, ExtensionToMatch)) - return PathExtension; - - return nullptr; - } - - if (PathLen == 0) - return nullptr; - - // Look for extension introducer ('.') - - for (int64_t i = PathLen - 1; i >= 0; --i) - { - if (Path[i] == '.') - return Path.data() + i; - } - - return nullptr; -} - -////////////////////////////////////////////////////////////////////////// - -void -Utf8ToWide(const char8_t* Str8, WideStringBuilderBase& OutString) -{ - Utf8ToWide(std::u8string_view(Str8), OutString); -} - -void -Utf8ToWide(const std::string_view& Str8, WideStringBuilderBase& OutString) -{ - Utf8ToWide(std::u8string_view{reinterpret_cast<const char8_t*>(Str8.data()), Str8.size()}, OutString); -} - -std::wstring -Utf8ToWide(const std::string_view& Wstr) -{ - ExtendableWideStringBuilder<128> String; - Utf8ToWide(Wstr, String); - - return String.c_str(); -} - -void -Utf8ToWide(const std::u8string_view& Str8, WideStringBuilderBase& OutString) -{ - const char* str = (const char*)Str8.data(); - const size_t strLen = Str8.size(); - - const char* endStr = str + strLen; - size_t ByteCount = 0; - size_t CurrentOutChar = 0; - - for (; str != endStr; ++str) - { - unsigned char Data = static_cast<unsigned char>(*str); - - if (!(Data & 0x80)) - { - // ASCII - OutString.Append(wchar_t(Data)); - continue; - } - else if (!ByteCount) - { - // Start of multi-byte sequence. Figure out how - // many bytes we're going to consume - - size_t Count = 0; - - for (size_t Temp = Data; Temp & 0x80; Temp <<= 1) - ++Count; - - ByteCount = Count - 1; - CurrentOutChar = Data & (0xff >> (Count + 1)); - } - else - { - --ByteCount; - - if ((Data & 0xc0) != 0x80) - { - break; - } - - CurrentOutChar = (CurrentOutChar << 6) | (Data & 0x3f); - - if (!ByteCount) - { - OutString.Append(wchar_t(CurrentOutChar)); - CurrentOutChar = 0; - } - } - } -} - -void -WideToUtf8(const wchar_t* Wstr, StringBuilderBase& OutString) -{ - WideToUtf8(std::wstring_view{Wstr}, OutString); -} - -void -WideToUtf8(const std::wstring_view& Wstr, StringBuilderBase& OutString) -{ -#if ZEN_SIZEOF_WCHAR_T == 2 - utf16to8_impl(begin(Wstr), end(Wstr), OutString); -#else - utf32to8_impl(begin(Wstr), end(Wstr), OutString); -#endif -} - -std::string -WideToUtf8(const wchar_t* Wstr) -{ - ExtendableStringBuilder<128> String; - WideToUtf8(std::wstring_view{Wstr}, String); - - return String.c_str(); -} - -std::string -WideToUtf8(const std::wstring_view Wstr) -{ - ExtendableStringBuilder<128> String; - WideToUtf8(std::wstring_view{Wstr.data(), Wstr.size()}, String); - - return String.c_str(); -} - -////////////////////////////////////////////////////////////////////////// - -enum NicenumFormat -{ - kNicenum1024 = 0, // Print kilo, mega, tera, peta, exa.. - kNicenumBytes = 1, // Print single bytes ("13B"), kilo, mega, tera... - kNicenumTime = 2, // Print nanosecs, microsecs, millisecs, seconds... - kNicenumRaw = 3, // Print the raw number without any formatting - kNicenumRawTime = 4 // Same as RAW, but print dashes ('-') for zero. -}; - -namespace { - static const char* UnitStrings[3][7] = { - /* kNicenum1024 */ {"", "K", "M", "G", "T", "P", "E"}, - /* kNicenumBytes */ {"B", "K", "M", "G", "T", "P", "E"}, - /* kNicenumTime */ {"ns", "us", "ms", "s", "?", "?", "?"}}; - - static const int UnitsLen[] = { - /* kNicenum1024 */ 6, - /* kNicenumBytes */ 6, - /* kNicenumTime */ 3}; - - static const uint64_t KiloUnit[] = { - /* kNicenum1024 */ 1024, - /* kNicenumBytes */ 1024, - /* kNicenumTime */ 1000}; -} // namespace - -/* - * Convert a number to an appropriately human-readable output. - */ -int -NiceNumGeneral(uint64_t Num, std::span<char> Buffer, NicenumFormat Format) -{ - switch (Format) - { - case kNicenumRaw: - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64, (uint64_t)Num); - - case kNicenumRawTime: - if (Num > 0) - { - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64, (uint64_t)Num); - } - else - { - return snprintf(Buffer.data(), Buffer.size(), "%s", "-"); - } - break; - - case kNicenum1024: - case kNicenumBytes: - case kNicenumTime: - default: - break; - } - - // Bring into range and select unit - - int Index = 0; - uint64_t n = Num; - - { - const uint64_t Unit = KiloUnit[Format]; - const int maxIndex = UnitsLen[Format]; - - while (n >= Unit && Index < maxIndex) - { - n /= Unit; - Index++; - } - } - - const char* u = UnitStrings[Format][Index]; - - if ((Index == 0) || ((Num % (uint64_t)powl((int)KiloUnit[Format], Index)) == 0)) - { - /* - * If this is an even multiple of the base, always display - * without any decimal precision. - */ - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64 "%s", (uint64_t)n, u); - } - else - { - /* - * We want to choose a precision that reflects the best choice - * for fitting in 5 characters. This can get rather tricky when - * we have numbers that are very close to an order of magnitude. - * For example, when displaying 10239 (which is really 9.999K), - * we want only a single place of precision for 10.0K. We could - * develop some complex heuristics for this, but it's much - * easier just to try each combination in turn. - */ - - int StrLen = 0; - - for (int i = 2; i >= 0; i--) - { - double Value = (double)Num / (uint64_t)powl((int)KiloUnit[Format], Index); - - /* - * Don't print floating point values for time. Note, - * we use floor() instead of round() here, since - * round can result in undesirable results. For - * example, if "num" is in the range of - * 999500-999999, it will print out "1000us". This - * doesn't happen if we use floor(). - */ - if (Format == kNicenumTime) - { - StrLen = snprintf(Buffer.data(), Buffer.size(), "%d%s", (unsigned int)floor(Value), u); - - if (StrLen <= 5) - break; - } - else - { - StrLen = snprintf(Buffer.data(), Buffer.size(), "%.*f%s", i, Value, u); - - if (StrLen <= 5) - break; - } - } - - return StrLen; - } -} - -size_t -NiceNumToBuffer(uint64_t Num, std::span<char> Buffer) -{ - return NiceNumGeneral(Num, Buffer, kNicenum1024); -} - -size_t -NiceBytesToBuffer(uint64_t Num, std::span<char> Buffer) -{ - return NiceNumGeneral(Num, Buffer, kNicenumBytes); -} - -size_t -NiceByteRateToBuffer(uint64_t Num, uint64_t ElapsedMs, std::span<char> Buffer) -{ - size_t n = 0; - - if (ElapsedMs) - { - n = NiceNumGeneral(Num * 1000 / ElapsedMs, Buffer, kNicenumBytes); - } - else - { - Buffer[n++] = '0'; - Buffer[n++] = 'B'; - } - - Buffer[n++] = '/'; - Buffer[n++] = 's'; - Buffer[n++] = '\0'; - - return n; -} - -size_t -NiceLatencyNsToBuffer(uint64_t Nanos, std::span<char> Buffer) -{ - return NiceNumGeneral(Nanos, Buffer, kNicenumTime); -} - -size_t -NiceTimeSpanMsToBuffer(uint64_t Millis, std::span<char> Buffer) -{ - if (Millis < 1000) - { - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64 "ms", Millis); - } - else if (Millis < 10000) - { - return snprintf(Buffer.data(), Buffer.size(), "%.2fs", Millis / 1000.0); - } - else if (Millis < 60000) - { - return snprintf(Buffer.data(), Buffer.size(), "%.1fs", Millis / 1000.0); - } - else if (Millis < 60 * 60000) - { - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64 "m%02" PRIu64 "s", Millis / 60000, (Millis / 1000) % 60); - } - else - { - return snprintf(Buffer.data(), Buffer.size(), "%" PRIu64 "h%02" PRIu64 "m", Millis / 3600000, (Millis / 60000) % 60); - } -} - -////////////////////////////////////////////////////////////////////////// - -template<typename C> -StringBuilderImpl<C>::~StringBuilderImpl() -{ - if (m_IsDynamic) - { - FreeBuffer(m_Base, m_End - m_Base); - } -} - -template<typename C> -void -StringBuilderImpl<C>::Extend(size_t extraCapacity) -{ - if (!m_IsExtendable) - { - Fail("exceeded capacity"); - } - - const size_t oldCapacity = m_End - m_Base; - const size_t newCapacity = NextPow2(oldCapacity + extraCapacity); - - C* newBase = (C*)AllocBuffer(newCapacity); - - size_t pos = m_CurPos - m_Base; - memcpy(newBase, m_Base, pos * sizeof(C)); - - if (m_IsDynamic) - { - FreeBuffer(m_Base, oldCapacity); - } - - m_Base = newBase; - m_CurPos = newBase + pos; - m_End = newBase + newCapacity; - m_IsDynamic = true; -} - -template<typename C> -void* -StringBuilderImpl<C>::AllocBuffer(size_t byteCount) -{ - return Memory::Alloc(byteCount * sizeof(C)); -} - -template<typename C> -void -StringBuilderImpl<C>::FreeBuffer(void* buffer, size_t byteCount) -{ - ZEN_UNUSED(byteCount); - - Memory::Free(buffer); -} - -template<typename C> -[[noreturn]] void -StringBuilderImpl<C>::Fail(const char* reason) -{ - throw std::runtime_error(reason); -} - -// Instantiate templates once - -template class StringBuilderImpl<char>; -template class StringBuilderImpl<wchar_t>; - -////////////////////////////////////////////////////////////////////////// -// -// Unit tests -// - -#if ZEN_WITH_TESTS - -TEST_CASE("niceNum") -{ - char Buffer[16]; - - SUBCASE("raw") - { - NiceNumGeneral(1, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "1")); - - NiceNumGeneral(10, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "10")); - - NiceNumGeneral(100, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "100")); - - NiceNumGeneral(1000, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "1000")); - - NiceNumGeneral(10000, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "10000")); - - NiceNumGeneral(100000, Buffer, kNicenumRaw); - CHECK(StringEquals(Buffer, "100000")); - } - - SUBCASE("1024") - { - NiceNumGeneral(1, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1")); - - NiceNumGeneral(10, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "10")); - - NiceNumGeneral(100, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "100")); - - NiceNumGeneral(1000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1000")); - - NiceNumGeneral(10000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.77K")); - - NiceNumGeneral(100000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "97.7K")); - - NiceNumGeneral(1000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "977K")); - - NiceNumGeneral(10000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.54M")); - - NiceNumGeneral(100000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "95.4M")); - - NiceNumGeneral(1000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "954M")); - - NiceNumGeneral(10000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.31G")); - - NiceNumGeneral(100000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "93.1G")); - - NiceNumGeneral(1000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "931G")); - - NiceNumGeneral(10000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.09T")); - - NiceNumGeneral(100000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "90.9T")); - - NiceNumGeneral(1000000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "909T")); - - NiceNumGeneral(10000000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "8.88P")); - - NiceNumGeneral(100000000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "88.8P")); - - NiceNumGeneral(1000000000000000000, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "888P")); - - NiceNumGeneral(10000000000000000000ull, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "8.67E")); - - // pow2 - - NiceNumGeneral(0, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "0")); - - NiceNumGeneral(1, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1")); - - NiceNumGeneral(1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1K")); - - NiceNumGeneral(1024 * 1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1M")); - - NiceNumGeneral(1024 * 1024 * 1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1G")); - - NiceNumGeneral(1024llu * 1024 * 1024 * 1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1T")); - - NiceNumGeneral(1024llu * 1024 * 1024 * 1024 * 1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1P")); - - NiceNumGeneral(1024llu * 1024 * 1024 * 1024 * 1024 * 1024, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1E")); - - // pow2-1 - - NiceNumGeneral(1023, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "1023")); - - NiceNumGeneral(2047, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "2.00K")); - - NiceNumGeneral(9 * 1024 - 1, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.00K")); - - NiceNumGeneral(10 * 1024 - 1, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "10.0K")); - - NiceNumGeneral(10 * 1024 - 5, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "10.0K")); - - NiceNumGeneral(10 * 1024 - 6, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.99K")); - - NiceNumGeneral(10 * 1024 - 10, Buffer, kNicenum1024); - CHECK(StringEquals(Buffer, "9.99K")); - } - - SUBCASE("time") - { - NiceNumGeneral(1, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "1ns")); - - NiceNumGeneral(100, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "100ns")); - - NiceNumGeneral(1000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "1us")); - - NiceNumGeneral(10000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "10us")); - - NiceNumGeneral(100000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "100us")); - - NiceNumGeneral(1000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "1ms")); - - NiceNumGeneral(10000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "10ms")); - - NiceNumGeneral(100000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "100ms")); - - NiceNumGeneral(1000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "1s")); - - NiceNumGeneral(10000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "10s")); - - NiceNumGeneral(100000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "100s")); - - NiceNumGeneral(1000000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "1000s")); - - NiceNumGeneral(10000000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "10000s")); - - NiceNumGeneral(100000000000000, Buffer, kNicenumTime); - CHECK(StringEquals(Buffer, "100000s")); - } - - SUBCASE("bytes") - { - NiceNumGeneral(1, Buffer, kNicenumBytes); - CHECK(StringEquals(Buffer, "1B")); - - NiceNumGeneral(10, Buffer, kNicenumBytes); - CHECK(StringEquals(Buffer, "10B")); - - NiceNumGeneral(100, Buffer, kNicenumBytes); - CHECK(StringEquals(Buffer, "100B")); - - NiceNumGeneral(1000, Buffer, kNicenumBytes); - CHECK(StringEquals(Buffer, "1000B")); - - NiceNumGeneral(10000, Buffer, kNicenumBytes); - CHECK(StringEquals(Buffer, "9.77K")); - } - - SUBCASE("byteRate") - { - NiceByteRateToBuffer(1, 1, Buffer); - CHECK(StringEquals(Buffer, "1000B/s")); - - NiceByteRateToBuffer(1000, 1000, Buffer); - CHECK(StringEquals(Buffer, "1000B/s")); - - NiceByteRateToBuffer(1024, 1, Buffer); - CHECK(StringEquals(Buffer, "1000K/s")); - - NiceByteRateToBuffer(1024, 1000, Buffer); - CHECK(StringEquals(Buffer, "1K/s")); - } - - SUBCASE("timespan") - { - NiceTimeSpanMsToBuffer(1, Buffer); - CHECK(StringEquals(Buffer, "1ms")); - - NiceTimeSpanMsToBuffer(900, Buffer); - CHECK(StringEquals(Buffer, "900ms")); - - NiceTimeSpanMsToBuffer(1000, Buffer); - CHECK(StringEquals(Buffer, "1.00s")); - - NiceTimeSpanMsToBuffer(1900, Buffer); - CHECK(StringEquals(Buffer, "1.90s")); - - NiceTimeSpanMsToBuffer(19000, Buffer); - CHECK(StringEquals(Buffer, "19.0s")); - - NiceTimeSpanMsToBuffer(60000, Buffer); - CHECK(StringEquals(Buffer, "1m00s")); - - NiceTimeSpanMsToBuffer(600000, Buffer); - CHECK(StringEquals(Buffer, "10m00s")); - - NiceTimeSpanMsToBuffer(3600000, Buffer); - CHECK(StringEquals(Buffer, "1h00m")); - - NiceTimeSpanMsToBuffer(36000000, Buffer); - CHECK(StringEquals(Buffer, "10h00m")); - - NiceTimeSpanMsToBuffer(360000000, Buffer); - CHECK(StringEquals(Buffer, "100h00m")); - } -} - -void -string_forcelink() -{ -} - -TEST_CASE("StringBuilder") -{ - StringBuilder<64> sb; - - SUBCASE("Empty init") - { - const char* str = sb.c_str(); - - CHECK(StringLength(str) == 0); - } - - SUBCASE("Append single character") - { - sb.Append('a'); - - const char* str = sb.c_str(); - CHECK(StringLength(str) == 1); - CHECK(str[0] == 'a'); - - sb.Append('b'); - str = sb.c_str(); - CHECK(StringLength(str) == 2); - CHECK(str[0] == 'a'); - CHECK(str[1] == 'b'); - } - - SUBCASE("Append string") - { - sb.Append("a"); - - const char* str = sb.c_str(); - CHECK(StringLength(str) == 1); - CHECK(str[0] == 'a'); - - sb.Append("b"); - str = sb.c_str(); - CHECK(StringLength(str) == 2); - CHECK(str[0] == 'a'); - CHECK(str[1] == 'b'); - - sb.Append("cdefghijklmnopqrstuvwxyz"); - CHECK(sb.Size() == 26); - - sb.Append("abcdefghijklmnopqrstuvwxyz"); - CHECK(sb.Size() == 52); - - sb.Append("abcdefghijk"); - CHECK(sb.Size() == 63); - } -} - -TEST_CASE("ExtendableStringBuilder") -{ - ExtendableStringBuilder<16> sb; - - SUBCASE("Empty init") - { - const char* str = sb.c_str(); - - CHECK(StringLength(str) == 0); - } - - SUBCASE("Short append") - { - sb.Append("abcd"); - CHECK(sb.IsDynamic() == false); - } - - SUBCASE("Short+long append") - { - sb.Append("abcd"); - CHECK(sb.IsDynamic() == false); - // This should trigger a dynamic buffer allocation since the required - // capacity exceeds the internal fixed buffer. - sb.Append("abcdefghijklmnopqrstuvwxyz"); - CHECK(sb.IsDynamic() == true); - CHECK(sb.Size() == 30); - CHECK(sb.Size() == StringLength(sb.c_str())); - } -} - -TEST_CASE("WideStringBuilder") -{ - WideStringBuilder<64> sb; - - SUBCASE("Empty init") - { - const wchar_t* str = sb.c_str(); - - CHECK(StringLength(str) == 0); - } - - SUBCASE("Append single character") - { - sb.Append(L'a'); - - const wchar_t* str = sb.c_str(); - CHECK(StringLength(str) == 1); - CHECK(str[0] == L'a'); - - sb.Append(L'b'); - str = sb.c_str(); - CHECK(StringLength(str) == 2); - CHECK(str[0] == L'a'); - CHECK(str[1] == L'b'); - } - - SUBCASE("Append string") - { - sb.Append(L"a"); - - const wchar_t* str = sb.c_str(); - CHECK(StringLength(str) == 1); - CHECK(str[0] == L'a'); - - sb.Append(L"b"); - str = sb.c_str(); - CHECK(StringLength(str) == 2); - CHECK(str[0] == L'a'); - CHECK(str[1] == L'b'); - - sb.Append(L"cdefghijklmnopqrstuvwxyz"); - CHECK(sb.Size() == 26); - - sb.Append(L"abcdefghijklmnopqrstuvwxyz"); - CHECK(sb.Size() == 52); - - sb.Append(L"abcdefghijk"); - CHECK(sb.Size() == 63); - } -} - -TEST_CASE("ExtendableWideStringBuilder") -{ - ExtendableWideStringBuilder<16> sb; - - SUBCASE("Empty init") - { - CHECK(sb.Size() == 0); - - const wchar_t* str = sb.c_str(); - CHECK(StringLength(str) == 0); - } - - SUBCASE("Short append") - { - sb.Append(L"abcd"); - CHECK(sb.IsDynamic() == false); - } - - SUBCASE("Short+long append") - { - sb.Append(L"abcd"); - CHECK(sb.IsDynamic() == false); - // This should trigger a dynamic buffer allocation since the required - // capacity exceeds the internal fixed buffer. - sb.Append(L"abcdefghijklmnopqrstuvwxyz"); - CHECK(sb.IsDynamic() == true); - CHECK(sb.Size() == 30); - CHECK(sb.Size() == StringLength(sb.c_str())); - } -} - -TEST_CASE("utf8") -{ - SUBCASE("utf8towide") - { - // TODO: add more extensive testing here - this covers a very small space - - WideStringBuilder<32> wout; - Utf8ToWide(u8"abcdefghi", wout); - CHECK(StringEquals(L"abcdefghi", wout.c_str())); - - wout.Reset(); - - Utf8ToWide(u8"abc���", wout); - CHECK(StringEquals(L"abc���", wout.c_str())); - } - - SUBCASE("widetoutf8") - { - // TODO: add more extensive testing here - this covers a very small space - - StringBuilder<32> out; - - WideToUtf8(L"abcdefghi", out); - CHECK(StringEquals("abcdefghi", out.c_str())); - - out.Reset(); - - WideToUtf8(L"abc���", out); - CHECK(StringEquals(u8"abc���", out.c_str())); - } -} - -TEST_CASE("filepath") -{ - CHECK(FilepathFindExtension("foo\\bar\\baz.txt", ".txt") != nullptr); - CHECK(FilepathFindExtension("foo\\bar\\baz.txt", ".zap") == nullptr); - - CHECK(FilepathFindExtension("foo\\bar\\baz.txt") != nullptr); - CHECK(FilepathFindExtension("foo\\bar\\baz.txt") == std::string_view(".txt")); - - CHECK(FilepathFindExtension(".txt") == std::string_view(".txt")); -} - -TEST_CASE("string") -{ - using namespace std::literals; - - SUBCASE("hash_djb2") - { - CHECK(HashStringAsLowerDjb2("AbcdZ"sv) == HashStringDjb2("abcdz"sv)); - CHECK(HashStringAsLowerDjb2("aBCd"sv) == HashStringDjb2("abcd"sv)); - CHECK(HashStringAsLowerDjb2("aBCd"sv) == HashStringDjb2(ToLower("aBCd"sv))); - } - - SUBCASE("tolower") - { - CHECK_EQ(ToLower("te!st"sv), "te!st"sv); - CHECK_EQ(ToLower("TE%St"sv), "te%st"sv); - } - - SUBCASE("StrCaseCompare") - { - CHECK(StrCaseCompare("foo", "FoO") == 0); - CHECK(StrCaseCompare("Bar", "bAs") < 0); - CHECK(StrCaseCompare("bAr", "Bas") < 0); - CHECK(StrCaseCompare("BBr", "Bar") > 0); - CHECK(StrCaseCompare("Bbr", "BAr") > 0); - CHECK(StrCaseCompare("foo", "FoO", 3) == 0); - CHECK(StrCaseCompare("Bar", "bAs", 3) < 0); - CHECK(StrCaseCompare("BBr", "Bar", 2) > 0); - } - - SUBCASE("ForEachStrTok") - { - const auto Tokens = "here,is,my,different,tokens"sv; - int32_t ExpectedTokenCount = 5; - int32_t TokenCount = 0; - StringBuilder<512> Sb; - - TokenCount = ForEachStrTok(Tokens, ',', [&Sb](const std::string_view& Token) { - if (Sb.Size()) - { - Sb << ","; - } - Sb << Token; - return true; - }); - - CHECK(TokenCount == ExpectedTokenCount); - CHECK(Sb.ToString() == Tokens); - - ExpectedTokenCount = 1; - const auto Str = "mosdef"sv; - - Sb.Reset(); - TokenCount = ForEachStrTok(Str, ' ', [&Sb](const std::string_view& Token) { - Sb << Token; - return true; - }); - CHECK(Sb.ToString() == Str); - CHECK(TokenCount == ExpectedTokenCount); - - ExpectedTokenCount = 0; - TokenCount = ForEachStrTok(""sv, ',', [](const std::string_view&) { return true; }); - CHECK(TokenCount == ExpectedTokenCount); - } -} - -#endif - -} // namespace zen |