/******************************************************************************* * This file is part of the "Enduro2D" * For conditions of distribution and use, see copyright notice in LICENSE.md * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ #include "_utils.hpp" using namespace e2d; namespace { const char* null_utf8 = nullptr; const wchar_t* null_wide = nullptr; const char16_t* null_utf16 = nullptr; const char32_t* null_utf32 = nullptr; str_view null_view = {null_utf8, 0}; } TEST_CASE("strings") { { REQUIRE(str_hash().empty()); REQUIRE(str_hash("").empty()); REQUIRE_FALSE(str_hash("1").empty()); REQUIRE(str_hash().hash() == str_hash().hash()); REQUIRE(str_hash().hash() == str_hash({null_utf8,0}).hash()); REQUIRE(str_hash().hash() == wstr_hash({null_wide,0}).hash()); REQUIRE(str_hash().hash() == str16_hash({null_utf16,0}).hash()); REQUIRE(str_hash().hash() == str32_hash({null_utf32,0}).hash()); REQUIRE(str_hash("hello").hash() == str_hash("hello").hash()); REQUIRE(str_hash("world").hash() == str_hash("world").hash()); REQUIRE(str_hash("hello").hash() != str_hash("world").hash()); REQUIRE(str_hash() == str_hash()); REQUIRE(str_hash() == make_hash({null_utf8,0})); REQUIRE(wstr_hash() == make_hash({null_wide,0})); REQUIRE(str16_hash() == make_hash({null_utf16,0})); REQUIRE(str32_hash() == make_hash({null_utf32,0})); REQUIRE(str_hash("hello") == make_hash("hello")); REQUIRE(str_hash("world") == make_hash("world")); REQUIRE(str_hash("hello") != make_hash("world")); REQUIRE(str_hash("hello").assign("world") == make_hash("world")); REQUIRE(str_hash("hello").assign(make_hash("world")) == make_hash("world")); { str_hash s("hello"); s.clear(); REQUIRE(s.empty()); REQUIRE(s == str_hash()); } { str_hash s1("hello"); str_hash s2("world"); s1.swap(s2); REQUIRE(s2 == str_hash("hello")); REQUIRE(s1 == make_hash("world")); } } { REQUIRE(make_utf8("hello") == "hello"); REQUIRE(make_utf8(L"hello") == "hello"); REQUIRE(make_utf8(u"hello") == "hello"); REQUIRE(make_utf8(U"hello") == "hello"); REQUIRE(make_wide("hello") == L"hello"); REQUIRE(make_wide(L"hello") == L"hello"); REQUIRE(make_wide(u"hello") == L"hello"); REQUIRE(make_wide(U"hello") == L"hello"); REQUIRE(make_utf16("hello") == u"hello"); REQUIRE(make_utf16(L"hello") == u"hello"); REQUIRE(make_utf16(u"hello") == u"hello"); REQUIRE(make_utf16(U"hello") == u"hello"); REQUIRE(make_utf32("hello") == U"hello"); REQUIRE(make_utf32(L"hello") == U"hello"); REQUIRE(make_utf32(u"hello") == U"hello"); REQUIRE(make_utf32(U"hello") == U"hello"); } { REQUIRE(make_utf8(str_view(null_utf8, 0)) == make_utf8(u"")); REQUIRE(make_utf8(wstr_view(null_wide, 0)) == make_utf8(L"")); REQUIRE(make_utf8(str16_view(null_utf16, 0)) == make_utf8(u"")); REQUIRE(make_utf8(str32_view(null_utf32, 0)) == make_utf8(U"")); REQUIRE(make_wide(str_view(null_utf8, 0)) == make_wide(u"")); REQUIRE(make_wide(wstr_view(null_wide, 0)) == make_wide(L"")); REQUIRE(make_wide(str16_view(null_utf16, 0)) == make_wide(u"")); REQUIRE(make_wide(str32_view(null_utf32, 0)) == make_wide(U"")); REQUIRE(make_utf16(str_view(null_utf8, 0)) == make_utf16(u"")); REQUIRE(make_utf16(wstr_view(null_wide, 0)) == make_utf16(L"")); REQUIRE(make_utf16(str16_view(null_utf16, 0)) == make_utf16(u"")); REQUIRE(make_utf16(str32_view(null_utf32, 0)) == make_utf16(U"")); REQUIRE(make_utf32(str_view(null_utf8, 0)) == make_utf32(u"")); REQUIRE(make_utf32(wstr_view(null_wide, 0)) == make_utf32(L"")); REQUIRE(make_utf32(str16_view(null_utf16, 0)) == make_utf32(u"")); REQUIRE(make_utf32(str32_view(null_utf32, 0)) == make_utf32(U"")); } { using strings::try_parse; { i8 v{111}; REQUIRE((try_parse("42", v) && v == 42)); REQUIRE((try_parse("127", v) && v == 127)); REQUIRE((try_parse("0", v) && v == 0)); REQUIRE((try_parse("-128", v) && v == -128)); u8 uv{111}; REQUIRE((try_parse("42", uv) && uv == 42)); REQUIRE((try_parse("255", uv) && uv == 255)); REQUIRE((try_parse("0", uv) && uv == 0)); } { i8 v{111}; REQUIRE((!try_parse(str_view(), v) && v == 111)); REQUIRE((!try_parse("", v) && v == 111)); REQUIRE((!try_parse(" \t", v) && v == 111)); REQUIRE((!try_parse("42hello", v) && v == 111)); REQUIRE((!try_parse("world42", v) && v == 111)); REQUIRE((!try_parse("42 ", v) && v == 111)); REQUIRE((!try_parse("-129", v) && v == 111)); REQUIRE((!try_parse("128", v) && v == 111)); REQUIRE((!try_parse("4.2", v) && v == 111)); u8 uv{111}; REQUIRE((!try_parse(str_view(), uv) && uv == 111)); REQUIRE((!try_parse("", uv) && uv == 111)); REQUIRE((!try_parse(" \t", uv) && uv == 111)); REQUIRE((!try_parse("42hello", uv) && uv == 111)); REQUIRE((!try_parse("world42", uv) && uv == 111)); REQUIRE((!try_parse("42 ", uv) && uv == 111)); REQUIRE((!try_parse("-1", uv) && uv == 111)); REQUIRE((!try_parse("256", uv) && uv == 111)); REQUIRE((!try_parse("4.2", uv) && uv == 111)); } { f32 v32{11.22f}; REQUIRE((try_parse("4.23E5", v32) && math::approximately(v32, 4.23e5f))); REQUIRE((try_parse("42", v32) && math::approximately(v32, 42.f))); REQUIRE((try_parse("-2.43", v32) && math::approximately(v32, -2.43f))); REQUIRE((try_parse("-24", v32) && math::approximately(v32, -24.f))); f64 v64{11.22f}; REQUIRE((try_parse("4.23E5", v64) && math::approximately(v64, 4.23e5))); REQUIRE((try_parse("42", v64) && math::approximately(v64, 42.))); REQUIRE((try_parse("-2.43", v64) && math::approximately(v64, -2.43))); REQUIRE((try_parse("-24", v64) && math::approximately(v64, -24.))); } { f32 v32{11.22f}; REQUIRE((!try_parse("", v32) && math::approximately(v32, 11.22f))); REQUIRE((!try_parse(" \t", v32) && math::approximately(v32, 11.22f))); REQUIRE((!try_parse("1.0E100", v32) && math::approximately(v32, 11.22f))); REQUIRE((!try_parse("1..4", v32) && math::approximately(v32, 11.22f))); REQUIRE((!try_parse("..14", v32) && math::approximately(v32, 11.22f))); REQUIRE((!try_parse("14..", v32) && math::approximately(v32, 11.22f))); f64 v64{11.22}; REQUIRE((!try_parse("", v64) && math::approximately(v64, 11.22))); REQUIRE((!try_parse(" \t", v64) && math::approximately(v64, 11.22))); REQUIRE((!try_parse("1.0E400", v64) && math::approximately(v64, 11.22))); REQUIRE((!try_parse("1..4", v64) && math::approximately(v64, 11.22))); REQUIRE((!try_parse("..14", v64) && math::approximately(v64, 11.22))); REQUIRE((!try_parse("14..", v64) && math::approximately(v64, 11.22))); } } { using strings::wildcard_match; char invalid_utf[] = "\xe6\x97\xa5\xd1\x88\xfa"; REQUIRE_THROWS_AS(wildcard_match(invalid_utf, "???"), std::exception); const auto mark_string = [](const char* str) -> str_view { static char string_buf[1024] = {0}; std::memset(string_buf, '*', sizeof(string_buf)); const std::size_t str_len = std::strlen(str); E2D_ASSERT(str_len < sizeof(string_buf)); std::memcpy(string_buf, str, str_len); return str_view(string_buf, str_len); }; const auto mark_pattern = [](const char* str) -> str_view { static char pattern_buf[1024] = {0}; std::memset(pattern_buf, '*', sizeof(pattern_buf)); const std::size_t str_len = std::strlen(str); E2D_ASSERT(str_len < sizeof(pattern_buf)); std::memcpy(pattern_buf, str, str_len); return str_view(pattern_buf, str_len); }; // 你好! REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"\u4F60\u597D!") == true); REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"?\u597D!") == true); REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"\u4F60?!") == true); REQUIRE(wildcard_match( // 你好你好你好你好世界世界世界世界世界世界世界世界彡ಠ mark_string("\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u5F61\u0CA0"), // 你好你好你好你好*世界世界彡*ಠ mark_pattern("\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4F60\u597D*\u4E16\u754C\u4E16\u754C\u5F61*\u0CA0")) == true); REQUIRE(wildcard_match("", "") == true); REQUIRE(wildcard_match("a", "") == false); REQUIRE(wildcard_match("", "*") == true); REQUIRE(wildcard_match("", "?") == false); REQUIRE(wildcard_match(null_view, null_view) == true); REQUIRE(wildcard_match("a", null_view) == false); REQUIRE(wildcard_match(null_view, "*") == true); REQUIRE(wildcard_match(null_view, "?") == false); // tests source: // http://developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("ab*d")) == false); REQUIRE(wildcard_match(mark_string("abcccd"), mark_pattern("*ccd")) == true); REQUIRE(wildcard_match(mark_string("mississipissippi"), mark_pattern("*issip*ss*")) == true); REQUIRE(wildcard_match(mark_string("xxxx*zzzzzzzzy*f"), mark_pattern("xxxx*zzy*fffff")) == false); REQUIRE(wildcard_match(mark_string("xxxx*zzzzzzzzy*f"), mark_pattern("xxx*zzy*f")) == true); REQUIRE(wildcard_match(mark_string("xxxxzzzzzzzzyf"), mark_pattern("xxxx*zzy*fffff")) == false); REQUIRE(wildcard_match(mark_string("xxxxzzzzzzzzyf"), mark_pattern("xxxx*zzy*f")) == true); REQUIRE(wildcard_match(mark_string("xyxyxyzyxyz"), mark_pattern("xy*z*xyz")) == true); REQUIRE(wildcard_match(mark_string("mississippi"), mark_pattern("*sip*")) == true); REQUIRE(wildcard_match(mark_string("xyxyxyxyz"), mark_pattern("xy*xyz")) == true); REQUIRE(wildcard_match(mark_string("mississippi"), mark_pattern("mi*sip*")) == true); REQUIRE(wildcard_match(mark_string("ababac"), mark_pattern("*abac*")) == true); REQUIRE(wildcard_match(mark_string("ababac"), mark_pattern("*abac*")) == true); REQUIRE(wildcard_match(mark_string("aaazz"), mark_pattern("a*zz*")) == true); REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("*12*23")) == false); REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("a12b")) == false); REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("*12*12*")) == true); REQUIRE(wildcard_match(mark_string("caaab"), mark_pattern("*a?b")) == true); REQUIRE(wildcard_match(mark_string("*"), mark_pattern("*")) == true); REQUIRE(wildcard_match(mark_string("a*abab"), mark_pattern("a*b")) == true); REQUIRE(wildcard_match(mark_string("a*r"), mark_pattern("a*")) == true); REQUIRE(wildcard_match(mark_string("a*ar"), mark_pattern("a*aar")) == false); REQUIRE(wildcard_match(mark_string("XYXYXYZYXYz"), mark_pattern("XY*Z*XYz")) == true); REQUIRE(wildcard_match(mark_string("missisSIPpi"), mark_pattern("*SIP*")) == true); REQUIRE(wildcard_match(mark_string("mississipPI"), mark_pattern("*issip*PI")) == true); REQUIRE(wildcard_match(mark_string("xyxyxyxyz"), mark_pattern("xy*xyz")) == true); REQUIRE(wildcard_match(mark_string("miSsissippi"), mark_pattern("mi*sip*")) == true); REQUIRE(wildcard_match(mark_string("miSsissippi"), mark_pattern("mi*Sip*")) == false); REQUIRE(wildcard_match(mark_string("abAbac"), mark_pattern("*Abac*")) == true); REQUIRE(wildcard_match(mark_string("abAbac"), mark_pattern("*Abac*")) == true); REQUIRE(wildcard_match(mark_string("aAazz"), mark_pattern("a*zz*")) == true); REQUIRE(wildcard_match(mark_string("A12b12"), mark_pattern("*12*23")) == false); REQUIRE(wildcard_match(mark_string("a12B12"), mark_pattern("*12*12*")) == true); REQUIRE(wildcard_match(mark_string("oWn"), mark_pattern("*oWn*")) == true); REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLah")) == true); REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLaH")) == false); REQUIRE(wildcard_match(mark_string("a"), mark_pattern("*?")) == true); REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("*?")) == true); REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("*?")) == true); REQUIRE(wildcard_match(mark_string("a"), mark_pattern("??")) == false); REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("?*?")) == true); REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("*?*?*")) == true); REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("?**?*?")) == true); REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("?**?*&?")) == false); REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?b*??")) == true); REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?a*??")) == false); REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?**?c?")) == true); REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?**?d?")) == false); REQUIRE(wildcard_match(mark_string("abcde"), mark_pattern("?*b*?*d*?")) == true); REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bL?h")) == true); REQUIRE(wildcard_match(mark_string("bLaaa"), mark_pattern("bLa?")) == false); REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLa?")) == true); REQUIRE(wildcard_match(mark_string("bLaH"), mark_pattern("?Lah")) == false); REQUIRE(wildcard_match(mark_string("bLaH"), mark_pattern("?LaH")) == true); REQUIRE(wildcard_match( mark_string("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"), mark_pattern("a*a*a*a*a*a*aa*aaa*a*a*b")) == true); REQUIRE(wildcard_match( mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"), mark_pattern("*a*b*ba*ca*a*aa*aaa*fa*ga*b*")) == true); REQUIRE(wildcard_match( mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"), mark_pattern("*a*b*ba*ca*a*x*aaa*fa*ga*b*")) == false); REQUIRE(wildcard_match( mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"), mark_pattern("*a*b*ba*ca*aaaa*fa*ga*gggg*b*")) == false); REQUIRE(wildcard_match( mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"), mark_pattern("*a*b*ba*ca*aaaa*fa*ga*ggg*b*")) == true); REQUIRE(wildcard_match( mark_string("aaabbaabbaab"), mark_pattern("*aabbaa*a*")) == true); REQUIRE(wildcard_match( mark_string("a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*"), mark_pattern("a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == true); REQUIRE(wildcard_match( mark_string("aaaaaaaaaaaaaaaaa"), mark_pattern("*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == true); REQUIRE(wildcard_match( mark_string("aaaaaaaaaaaaaaaa"), mark_pattern("*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == false); REQUIRE(wildcard_match( mark_string("abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn"), mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*")) == false); REQUIRE(wildcard_match( mark_string("abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn"), mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*")) == true); REQUIRE(wildcard_match( mark_string("abc*abcd*abcd*abc*abcd"), mark_pattern("abc*abc*abc*abc*abc")) == false); REQUIRE(wildcard_match( mark_string("abc*abcd*abcd*abc*abcd*abcd*abc*abcd*abc*abc*abcd"), mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abcd")) == true); REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("********a********b********c********")) == true); REQUIRE(wildcard_match(mark_string("********a********b********c********"), mark_pattern("abc")) == false); REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("********a********b********b********")) == false); REQUIRE(wildcard_match(mark_string("*abc*"), mark_pattern("***a*b*c***")) == true); } { REQUIRE(strings::starts_with("", "")); REQUIRE(strings::starts_with("hello_world", "")); REQUIRE(strings::starts_with("hello_world", "hello")); REQUIRE(strings::starts_with("hello_world", "hello_world")); REQUIRE_FALSE(strings::starts_with("", "a")); REQUIRE_FALSE(strings::starts_with("hello_world", "world")); REQUIRE_FALSE(strings::starts_with("hello_world", "hello_world_42")); REQUIRE_FALSE(strings::starts_with("hello_world", "42_hello_world")); REQUIRE(strings::ends_with("", "")); REQUIRE(strings::ends_with("hello_world", "")); REQUIRE(strings::ends_with("hello_world", "world")); REQUIRE(strings::ends_with("hello_world", "hello_world")); REQUIRE_FALSE(strings::ends_with("", "a")); REQUIRE_FALSE(strings::ends_with("hello_world", "hello")); REQUIRE_FALSE(strings::ends_with("hello_world", "hello_world_42")); REQUIRE_FALSE(strings::ends_with("hello_world", "42_hello_world")); } { char buf[6]; REQUIRE_THROWS_AS(strings::format(buf, 0, "hello"), strings::bad_format_buffer); REQUIRE_THROWS_AS(strings::format(nullptr, sizeof(buf), "hello"), strings::bad_format_buffer); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "helloE"), strings::bad_format_buffer); REQUIRE_NOTHROW(strings::format(buf, sizeof(buf), "hello")); REQUIRE_NOTHROW(strings::format(nullptr, 0, "hello")); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%hell"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%ll"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%10%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%10%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%10%hell"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%x%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%y%"), strings::bad_format); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%z%hell"), strings::bad_format); } { REQUIRE_THROWS_AS(strings::rformat("%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("%hell"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("he%ll"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("hell%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("%10%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("hell%10%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("%10%hell"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("%x%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("hell%y%"), strings::bad_format); REQUIRE_THROWS_AS(strings::rformat("%z%hell"), strings::bad_format); } { str s; REQUIRE(strings::rformat_nothrow(s, "%0", "hello")); REQUIRE(s == "hello"); REQUIRE_FALSE(strings::rformat_nothrow(s, "%")); char buf[5] = {0}; std::size_t length = 0; REQUIRE(strings::format_nothrow(buf, std::size(buf), &length, "%0", "hell")); REQUIRE(length == 4); REQUIRE(str(buf) == str("hell")); REQUIRE_FALSE(strings::format_nothrow(buf, std::size(buf), &length, "%0", "hello")); } { REQUIRE(strings::rformat(str_view("%0"), 42) == "42"); REQUIRE(strings::rformat(str_view("%0%1",2), 42) == "42"); auto s1 = make_utf8("hello"); auto s2 = make_wide("hello"); auto s3 = make_utf16("hello"); auto s4 = make_utf32("hello"); REQUIRE(strings::rformat(str_view("%0"), str_view(s1)) == "hello"); REQUIRE(strings::rformat(str_view("%0"), wstr_view(s2)) == "hello"); REQUIRE(strings::rformat(str_view("%0"), str16_view(s3)) == "hello"); REQUIRE(strings::rformat(str_view("%0"), str32_view(s4)) == "hello"); } { char buf[1]; std::memset(buf, 0xAB, sizeof(buf)); REQUIRE(strings::format(buf, sizeof(buf), "") == 0); REQUIRE(str(buf) == str("")); } { char buf[6]; std::memset(buf, 0xAB, sizeof(buf)); REQUIRE(strings::format(buf, sizeof(buf), "hello") == 5); REQUIRE(strings::format(nullptr, 0, "hello") == 5); REQUIRE(str(buf) == str("hello")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE(strings::format(buf, sizeof(buf), "hell%%") == 5); REQUIRE(strings::format(nullptr, 0, "hell%%") == 5); REQUIRE(str(buf) == str("hell%")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE(strings::format(buf, sizeof(buf), "%%hell") == 5); REQUIRE(strings::format(nullptr, 0, "%%hell") == 5); REQUIRE(str(buf) == str("%hell")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE(strings::format(buf, sizeof(buf), "he%%ll") == 5); REQUIRE(strings::format(nullptr, 0, "he%%ll") == 5); REQUIRE(str(buf) == str("he%ll")); } { char buf[5]; std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hello"), strings::bad_format_buffer); REQUIRE(str(buf) == str("hell")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%"), strings::bad_format); REQUIRE(str(buf) == str("he")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%99%"), strings::bad_format); REQUIRE(str(buf) == str("he")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%x%"), strings::bad_format); REQUIRE(str(buf) == str("he")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%0%", 1234), strings::bad_format_buffer); REQUIRE(str(buf) == str("he12")); } { char buf[10]; std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%0%", "hello world"), strings::bad_format_buffer); REQUIRE(str(buf) == str("hello wor")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "test%0%", "hello world"), strings::bad_format_buffer); REQUIRE(str(buf) == str("testhello")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%0%test", "hello world"), strings::bad_format_buffer); REQUIRE(str(buf) == str("hello wor")); std::memset(buf, 0xAB, sizeof(buf)); REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "te%0%st", "hello world"), strings::bad_format_buffer); REQUIRE(str(buf) == str("tehello w")); } { REQUIRE(strings::rformat("%0 %1 %2", "hello", "world", 5) == str("hello world 5")); REQUIRE(strings::rformat("%1 %0 %2", "hello", "world", 5) == str("world hello 5")); REQUIRE(strings::rformat("%2 %1 %0", "hello", "world", 5) == str("5 world hello")); REQUIRE(strings::rformat("%0 %0 %1", "hello", "world", 5) == str("hello hello world")); REQUIRE(strings::rformat("%2 %1 %1", "hello", "world", 5) == str("5 world world")); REQUIRE( strings::rformat( "%0 %2 %1 %4 %3 %6 %7 %5 %8 %9", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == str("0 2 1 4 3 6 7 5 8 9")); } { REQUIRE(strings::rformat("%0", static_cast(nullptr)).empty()); REQUIRE(strings::rformat("%0", static_cast(nullptr)).empty()); } { REQUIRE(strings::rformat("%0", strings::make_format_arg("ab", u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg("ab", u8(3))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg("ab", u8(2))) == "ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg("ab", u8(1))) == "ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg("ab", u8(0))) == "ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str("ab"), u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str("ab"), u8(3))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str("ab"), u8(2))) == "ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str("ab"), u8(1))) == "ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str("ab"), u8(0))) == "ab"); { auto s1 = make_utf8("ab"); auto s2 = make_wide("ab"); auto s3 = make_utf16("ab"); auto s4 = make_utf32("ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(s1, u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(s2, u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(s3, u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(s4, u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str_view(s1), u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(wstr_view(s2), u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str16_view(s3), u8(4))) == " ab"); REQUIRE(strings::rformat("%0", strings::make_format_arg(str32_view(s4), u8(4))) == " ab"); } } { REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, u8(3))) == " -5"); REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, u8(4))) == " -5"); REQUIRE(strings::rformat("%0", strings::make_format_arg(21, u8(1))) == "21"); REQUIRE(strings::rformat("%0", strings::make_format_arg(21, u8(2))) == "21"); REQUIRE(strings::rformat("%0", strings::make_format_arg(42, u8(3))) == " 42"); REQUIRE(strings::rformat("%0", strings::make_format_arg(42u, u8(3))) == " 42"); REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f)) == "1.230000"); REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(0))) == "1.230000"); REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(0),u8(2))) == "1.23"); REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(5),u8(2))) == " 1.23"); REQUIRE(strings::rformat("%0", strings::make_format_arg(true)) == "true"); REQUIRE(strings::rformat("%0", strings::make_format_arg(false)) == "false"); const char* s0 = "hello"; char s1[64]; std::strcpy(s1, "world"); REQUIRE(strings::rformat("%0", s0) == "hello"); REQUIRE(strings::rformat("%0", s1) == "world"); } { REQUIRE( strings::rformat("%0", std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); REQUIRE( strings::rformat("%0", std::numeric_limits::min()) == std::to_string(std::numeric_limits::min())); REQUIRE( strings::rformat("%0", std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); REQUIRE( strings::rformat("%0", std::numeric_limits::min()) == std::to_string(std::numeric_limits::min())); REQUIRE( strings::rformat("%0", std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); REQUIRE( strings::rformat("%0", std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); REQUIRE( strings::rformat("%0", std::numeric_limits::min()) == std::to_string(std::numeric_limits::min())); REQUIRE( strings::rformat("%0", std::numeric_limits::max()) == std::to_string(std::numeric_limits::max())); REQUIRE( strings::rformat("%0", std::numeric_limits::min()) == std::to_string(std::numeric_limits::min())); } SECTION("regression") { { // https://github.com/enduro2d/enduro2d/issues/10 { REQUIRE(strings::rformat("%0", "\0") == ""); REQUIRE(strings::format(nullptr, 0, "%0", "\0") == 0); } { char buf[1]; strings::format(buf, std::size(buf), "%0", "\0"); REQUIRE(str(buf) == str("")); } { char buf[2]; REQUIRE_THROWS_AS(strings::format(buf, std::size(buf), "%0%1", 1, 20), strings::bad_format_buffer); REQUIRE(str(buf) == str("1")); REQUIRE_THROWS_AS(strings::format(buf, std::size(buf), "%0%1", 20, 1), strings::bad_format_buffer); REQUIRE(str(buf) == str("2")); } } } SECTION("performance") { std::printf("-= strings::performance tests =-\n"); #if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG const std::size_t task_n = 100'000; #else const std::size_t task_n = 1'000'000; #endif { std::size_t result = 0; e2d_untests::verbose_profiler_ms p("format(int, int)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", 1000, 123); } p.done(result); } { std::ptrdiff_t result = 0; e2d_untests::verbose_profiler_ms p("snprintf(int, int)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += std::snprintf(buffer, sizeof(buffer), "hello %i world %i !", 1000, 123); } p.done(result); } { std::ptrdiff_t result = 0; e2d_untests::verbose_profiler_ms p("format(float, float)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", 1000.f, 123.f); } p.done(result); } { std::ptrdiff_t result = 0; e2d_untests::verbose_profiler_ms p("snprintf(float, float)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += std::snprintf(buffer, sizeof(buffer), "hello %f world %f !", 1000.0, 123.0); } p.done(result); } { std::ptrdiff_t result = 0; e2d_untests::verbose_profiler_ms p("format(const char*)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", "foo", "bar"); } p.done(result); } { std::ptrdiff_t result = 0; e2d_untests::verbose_profiler_ms p("snprintf(const char*)"); for ( std::size_t i = 0; i < task_n; ++i ) { char buffer[128]; result += std::snprintf(buffer, sizeof(buffer), "hello %s world %s !", "foo", "bar"); } p.done(result); } } }