From 08a18d1519dfe3ad18d421ac1cbe799687c77a08 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Thu, 8 Feb 2024 12:09:33 +0100 Subject: [PATCH] macOS support Signed-off-by: Simon Rozman --- UnitTests/UnitTests.xcodeproj/project.pbxproj | 6 + UnitTests/main.cpp | 7 +- UnitTests/parser.cpp | 152 ++++++------ UnitTests/pool.cpp | 22 +- UnitTests/ring.cpp | 8 +- UnitTests/stream.cpp | 44 ++-- UnitTests/string.cpp | 16 +- UnitTests/unicode.cpp | 5 +- include/stdex/html.hpp | 58 +++-- include/stdex/locale.hpp | 10 +- include/stdex/parser.hpp | 216 +++++++++--------- include/stdex/pool.hpp | 10 +- include/stdex/progress.hpp | 4 +- include/stdex/spinlock.hpp | 4 +- include/stdex/stream.hpp | 10 +- include/stdex/string.hpp | 33 ++- include/stdex/sys_info.hpp | 20 +- include/stdex/system.hpp | 2 +- include/stdex/unicode.hpp | 4 +- 19 files changed, 342 insertions(+), 289 deletions(-) diff --git a/UnitTests/UnitTests.xcodeproj/project.pbxproj b/UnitTests/UnitTests.xcodeproj/project.pbxproj index 3476eb9c1..fedf498b6 100644 --- a/UnitTests/UnitTests.xcodeproj/project.pbxproj +++ b/UnitTests/UnitTests.xcodeproj/project.pbxproj @@ -58,6 +58,9 @@ F4C07F582AB08E690044EDC0 /* sgml.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sgml.cpp; sourceTree = ""; }; F4C07F592AB08E690044EDC0 /* ring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ring.cpp; sourceTree = ""; }; F4C07F5A2AB08E690044EDC0 /* stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stream.cpp; sourceTree = ""; }; + F4CCA3B62B73B912007B857B /* watchdog.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = watchdog.cpp; sourceTree = ""; }; + F4CCA3B72B73B940007B857B /* pool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pool.cpp; sourceTree = ""; }; + F4CCA3B82B73D2E2007B857B /* string.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = string.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -110,12 +113,15 @@ F4C07F562AB08E690044EDC0 /* parser.cpp */, F4C07F512AB059580044EDC0 /* pch.cpp */, F4C07F502AB059580044EDC0 /* pch.hpp */, + F4CCA3B72B73B940007B857B /* pool.cpp */, F4B7FBDD2AAF49BC00C6BE9F /* Products */, F4C07F592AB08E690044EDC0 /* ring.cpp */, F4C07F582AB08E690044EDC0 /* sgml.cpp */, F4213D162ABB14AA00F72674 /* stdex */, F4C07F5A2AB08E690044EDC0 /* stream.cpp */, + F4CCA3B82B73D2E2007B857B /* string.cpp */, F4C07F572AB08E690044EDC0 /* unicode.cpp */, + F4CCA3B62B73B912007B857B /* watchdog.cpp */, ); sourceTree = ""; usesTabs = 1; diff --git a/UnitTests/main.cpp b/UnitTests/main.cpp index c1f027821..5f7d84ee2 100644 --- a/UnitTests/main.cpp +++ b/UnitTests/main.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -6,10 +6,12 @@ #include "pch.hpp" #include "hash.cpp" #include "math.cpp" +#include "pool.cpp" #include "parser.cpp" #include "ring.cpp" #include "sgml.cpp" #include "stream.cpp" +#include "string.cpp" #include "unicode.cpp" #include "watchdog.cpp" #include @@ -25,6 +27,7 @@ int main(int argc, const char * argv[]) UnitTests::parser::wtest(); UnitTests::parser::sgml_test(); UnitTests::parser::http_test(); + UnitTests::pool::test(); UnitTests::ring::test(); UnitTests::sgml::sgml2str(); UnitTests::sgml::str2sgml(); @@ -32,9 +35,11 @@ int main(int argc, const char * argv[]) UnitTests::stream::replicator(); UnitTests::stream::open_close(); UnitTests::stream::file_stat(); + UnitTests::string::sprintf(); UnitTests::unicode::str2wstr(); UnitTests::unicode::wstr2str(); UnitTests::unicode::charset_encoder(); + UnitTests::unicode::normalize(); UnitTests::watchdog::test(); std::cout << "PASS\n"; return 0; diff --git a/UnitTests/parser.cpp b/UnitTests/parser.cpp index 2256f48e6..b8e6209a9 100644 --- a/UnitTests/parser.cpp +++ b/UnitTests/parser.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -6,15 +6,13 @@ #include "pch.hpp" using namespace std; -using namespace stdex; -using namespace stdex::parser; #ifdef _WIN32 using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework { - static std::wstring ToString(const stdex::interval& q) + static wstring ToString(const stdex::interval& q) { return stdex::sprintf(L"<%zu, %zu>", nullptr, q.start, q.end); } @@ -33,22 +31,22 @@ namespace UnitTests static const wchar_t text[] = L"This is a test.\nSecond line."; { - wnoop p; + stdex::parser::wnoop p; Assert::IsTrue(p.match(text)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)0, p.interval.end); } { - wcu p(L't'); + stdex::parser::wcu p(L't'); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)1, p.interval.end); } { - wspace_cu p; + stdex::parser::wspace_cu p; Assert::IsFalse(p.match(text)); Assert::IsTrue(p.match(text, 4)); Assert::AreEqual((size_t)4, p.interval.start); @@ -56,7 +54,7 @@ namespace UnitTests } { - wpunct_cu p; + stdex::parser::wpunct_cu p; Assert::IsFalse(p.match(text)); Assert::IsTrue(p.match(text, 14)); Assert::AreEqual((size_t)14, p.interval.start); @@ -64,7 +62,7 @@ namespace UnitTests } { - wspace_or_punct_cu p; + stdex::parser::wspace_or_punct_cu p; Assert::IsFalse(p.match(text)); Assert::IsTrue(p.match(text, 4)); Assert::AreEqual((size_t)4, p.interval.start); @@ -75,7 +73,7 @@ namespace UnitTests } { - wbol p; + stdex::parser::wbol p; Assert::IsTrue(p.match(text)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)0, p.interval.end); @@ -87,7 +85,7 @@ namespace UnitTests } { - weol p; + stdex::parser::weol p; Assert::IsFalse(p.match(text)); Assert::IsFalse(p.match(text, 1)); Assert::IsTrue(p.match(text, 15)); @@ -97,14 +95,14 @@ namespace UnitTests } { - wcu_set p(L"abcD"); + stdex::parser::wcu_set p(L"abcD"); Assert::IsFalse(p.match(text)); Assert::IsTrue(p.match(text, 8)); Assert::AreEqual((size_t)8, p.interval.start); Assert::AreEqual((size_t)9, p.interval.end); Assert::AreEqual((size_t)0, p.hit_offset); Assert::IsFalse(p.match(text, 21)); - Assert::IsTrue(p.match(text, 21, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 21, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)21, p.interval.start); Assert::AreEqual((size_t)22, p.interval.end); Assert::AreEqual((size_t)3, p.hit_offset); @@ -113,83 +111,83 @@ namespace UnitTests { stdex::parser::wstring p(L"this"); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, sizeof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, sizeof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)4, p.interval.end); } { - wany_cu chr; - witerations p(make_shared_no_delete(&chr), 1, 5); + stdex::parser::wany_cu chr; + stdex::parser::witerations p(stdex::make_shared_no_delete(&chr), 1, 5); Assert::IsTrue(p.match(text)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)5, p.interval.end); } { - wspace_cu nospace(true); - witerations p(make_shared_no_delete(&nospace), 1); + stdex::parser::wspace_cu nospace(true); + stdex::parser::witerations p(stdex::make_shared_no_delete(&nospace), 1); Assert::IsTrue(p.match(text)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)4, p.interval.end); } { - wcu chr_t(L't'), chr_h(L'h'), chr_i(L'i'), chr_s(L's'); - wspace_cu space; - wsequence p({ - make_shared_no_delete(&chr_t), - make_shared_no_delete(&chr_h), - make_shared_no_delete(&chr_i), - make_shared_no_delete(&chr_s), - make_shared_no_delete(&space) }); + stdex::parser::wcu chr_t(L't'), chr_h(L'h'), chr_i(L'i'), chr_s(L's'); + stdex::parser::wspace_cu space; + stdex::parser::wsequence p({ + stdex::make_shared_no_delete(&chr_t), + stdex::make_shared_no_delete(&chr_h), + stdex::make_shared_no_delete(&chr_i), + stdex::make_shared_no_delete(&chr_s), + stdex::make_shared_no_delete(&space) }); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)5, p.interval.end); } { stdex::parser::wstring apple(L"apple"), orange(L"orange"), _this(L"this"); - wspace_cu space; - wbranch p({ - make_shared_no_delete(&apple), - make_shared_no_delete(&orange), - make_shared_no_delete(&_this), - make_shared_no_delete(&space) }); + stdex::parser::wspace_cu space; + stdex::parser::wbranch p({ + stdex::make_shared_no_delete(&apple), + stdex::make_shared_no_delete(&orange), + stdex::make_shared_no_delete(&_this), + stdex::make_shared_no_delete(&space) }); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)2, p.hit_offset); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)4, p.interval.end); } { - wstring_branch p(L"apple", L"orange", L"this", nullptr); + stdex::parser::wstring_branch p(L"apple", L"orange", L"this", nullptr); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)2, p.hit_offset); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)4, p.interval.end); } { - wcu chr_s(L's'), chr_h(L'h'), chr_i(L'i'), chr_t(L't'); - wpermutation p({ - make_shared_no_delete(&chr_s), - make_shared_no_delete(&chr_h), - make_shared_no_delete(&chr_i), - make_shared_no_delete(&chr_t) }); + stdex::parser::wcu chr_s(L's'), chr_h(L'h'), chr_i(L'i'), chr_t(L't'); + stdex::parser::wpermutation p({ + stdex::make_shared_no_delete(&chr_s), + stdex::make_shared_no_delete(&chr_h), + stdex::make_shared_no_delete(&chr_i), + stdex::make_shared_no_delete(&chr_t) }); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)4, p.interval.end); } { std::locale locale_slSI("sl_SI"); - wspace_cu space(false, locale_slSI); - wiban p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::wspace_cu space(false, locale_slSI); + stdex::parser::wiban p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match(L"SI56023120015226972", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual(L"SI", p.country); @@ -202,7 +200,7 @@ namespace UnitTests Assert::AreEqual(L"023120015226972", p.bban); Assert::IsFalse(p.match(L"si56 0231 2001 5226 972", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); - Assert::IsTrue(p.match(L"si56 0231 2001 5226 972", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match(L"si56 0231 2001 5226 972", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.is_valid); Assert::IsTrue(p.match(L"SI56 0231 2001 5226 9720", 0, SIZE_MAX)); Assert::AreEqual(stdex::interval(0, 23), p.interval); @@ -250,8 +248,8 @@ namespace UnitTests { std::locale locale_slSI("sl_SI"); - wspace_cu space(false, locale_slSI); - wcreditor_reference p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::wspace_cu space(false, locale_slSI); + stdex::parser::wcreditor_reference p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match(L"RF18539007547034", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual(L"18", p.check_digits); @@ -262,7 +260,7 @@ namespace UnitTests Assert::AreEqual(L"000000000539007547034", p.reference); Assert::IsFalse(p.match(L"rf18 5390 0754 7034", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); - Assert::IsTrue(p.match(L"rf18 5390 0754 7034", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match(L"rf18 5390 0754 7034", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.is_valid); Assert::IsTrue(p.match(L"RF18 5390 0754 70340", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); @@ -274,8 +272,8 @@ namespace UnitTests { std::locale locale_slSI("sl_SI"); - wspace_cu space(false, locale_slSI); - wsi_reference p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::wspace_cu space(false, locale_slSI); + stdex::parser::wsi_reference p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match(L"SI121234567890120", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual(L"12", p.model); @@ -285,7 +283,7 @@ namespace UnitTests Assert::AreEqual(L"12", p.model); Assert::AreEqual(stdex::interval(5, 18), p.part1.interval); Assert::IsFalse(p.match(L"si12 1234567890120", 0, SIZE_MAX)); - Assert::IsTrue(p.match(L"si12 1234567890120", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match(L"si12 1234567890120", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.match(L"...SI12 1234567890120...", 3, SIZE_MAX)); Assert::IsTrue(p.match(L"SI12 1234567890120", 0, SIZE_MAX)); // no-break space } @@ -297,30 +295,30 @@ namespace UnitTests static const char text[] = "V kožuščku zlobnega mizarja stopiclja fant\nin kliče 1234567890."; { - sgml_noop p; + stdex::parser::sgml_noop p; Assert::IsTrue(p.match(text)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)0, p.interval.end); } { - sgml_cp p("v"); + stdex::parser::sgml_cp p("v"); Assert::IsFalse(p.match(text)); - Assert::IsTrue(p.match(text, 0, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 0, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)1, p.interval.end); } { - sgml_cp p("Ž", SIZE_MAX, false, locale_slSI); + stdex::parser::sgml_cp p("Ž", SIZE_MAX, false, locale_slSI); Assert::IsFalse(p.match(text, 4)); - Assert::IsTrue(p.match(text, 4, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 4, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)4, p.interval.start); Assert::AreEqual((size_t)12, p.interval.end); } { - sgml_space_cp p(false, locale_slSI); + stdex::parser::sgml_space_cp p(false, locale_slSI); Assert::IsFalse(p.match(text)); Assert::IsTrue(p.match(text, 1)); Assert::AreEqual((size_t)1, p.interval.start); @@ -331,17 +329,17 @@ namespace UnitTests } { - sgml_string_branch p(locale_slSI, "apple", "orange", "KoŽuŠčKu", nullptr); + stdex::parser::sgml_string_branch p(locale_slSI, "apple", "orange", "KoŽuŠčKu", nullptr); Assert::IsFalse(p.match(text, 2)); - Assert::IsTrue(p.match(text, 2, _countof(text), match_case_insensitive)); + Assert::IsTrue(p.match(text, 2, _countof(text), stdex::parser::match_case_insensitive)); Assert::AreEqual((size_t)2, p.hit_offset); Assert::AreEqual((size_t)2, p.interval.start); Assert::AreEqual((size_t)31, p.interval.end); } { - sgml_space_cp space(false, locale_slSI); - sgml_iban p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::sgml_space_cp space(false, locale_slSI); + stdex::parser::sgml_iban p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match("SI56023120015226972", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual("SI", p.country); @@ -354,7 +352,7 @@ namespace UnitTests Assert::AreEqual("023120015226972", p.bban); Assert::IsFalse(p.match("si56 0231 2001 5226 972", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); - Assert::IsTrue(p.match("si56 0231 2001 5226 972", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match("si56 0231 2001 5226 972", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.is_valid); Assert::IsTrue(p.match("SI56 0231 2001 5226 9720", 0, SIZE_MAX)); Assert::AreEqual(stdex::interval(0, 23), p.interval); @@ -366,8 +364,8 @@ namespace UnitTests } { - sgml_space_cp space(false, locale_slSI); - sgml_creditor_reference p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::sgml_space_cp space(false, locale_slSI); + stdex::parser::sgml_creditor_reference p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match("RF18539007547034", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual("18", p.check_digits); @@ -378,7 +376,7 @@ namespace UnitTests Assert::AreEqual("000000000539007547034", p.reference); Assert::IsFalse(p.match("rf18 5390 0754 7034", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); - Assert::IsTrue(p.match("rf18 5390 0754 7034", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match("rf18 5390 0754 7034", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.is_valid); Assert::IsTrue(p.match("RF18 5390 0754 70340", 0, SIZE_MAX)); Assert::IsFalse(p.is_valid); @@ -389,8 +387,8 @@ namespace UnitTests } { - sgml_space_cp space(false, locale_slSI); - sgml_si_reference p(make_shared_no_delete(&space), locale_slSI); + stdex::parser::sgml_space_cp space(false, locale_slSI); + stdex::parser::sgml_si_reference p(stdex::make_shared_no_delete(&space), locale_slSI); Assert::IsTrue(p.match("SI121234567890120", 0, SIZE_MAX)); Assert::IsTrue(p.is_valid); Assert::AreEqual("12", p.model); @@ -400,7 +398,7 @@ namespace UnitTests Assert::AreEqual("12", p.model); Assert::AreEqual(stdex::interval(5, 18), p.part1.interval); Assert::IsFalse(p.match("si12 1234567890120", 0, SIZE_MAX)); - Assert::IsTrue(p.match("si12 1234567890120", 0, SIZE_MAX, match_case_insensitive)); + Assert::IsTrue(p.match("si12 1234567890120", 0, SIZE_MAX, stdex::parser::match_case_insensitive)); Assert::IsTrue(p.match("...SI12 1234567890120...", 3, SIZE_MAX)); Assert::IsTrue(p.match("SI12 1234567890120", 0, SIZE_MAX)); } @@ -429,7 +427,7 @@ namespace UnitTests "\r\n"; { - http_request p(locale); + stdex::parser::http_request p(locale); Assert::IsTrue(p.match(request)); Assert::AreEqual((size_t)0, p.interval.start); Assert::AreEqual((size_t)14, p.interval.end); @@ -443,10 +441,10 @@ namespace UnitTests } { - std::list hdrs; + list hdrs; size_t offset = 14; for (;;) { - http_header h; + stdex::parser::http_header h; if (h.match(request, offset)) { offset = h.interval.end; hdrs.push_back(std::move(h)); @@ -455,19 +453,19 @@ namespace UnitTests break; } Assert::AreEqual((size_t)15, hdrs.size()); - http_weighted_collection> langs; + stdex::parser::http_weighted_collection> langs; for (const auto& h : hdrs) - if (strnicmp(request + h.name.start, h.name.size(), "Accept-Language", SIZE_MAX, locale) == 0) + if (stdex::strnicmp(request + h.name.start, h.name.size(), "Accept-Language", SIZE_MAX, locale) == 0) langs.insert(request, h.value.start, h.value.end); Assert::IsTrue(!langs.empty()); { - const vector control = { + const vector control = { "sl", "en-US", "en", "de-DE", "de" }; auto c = control.cbegin(); auto l = langs.cbegin(); for (; c != control.cend() && l != langs.cend(); ++c, ++l) - Assert::IsTrue(strnicmp(request + l->value.interval.start, l->value.interval.size(), c->c_str(), c->size(), locale) == 0); + Assert::IsTrue(stdex::strnicmp(request + l->value.interval.start, l->value.interval.size(), c->c_str(), c->size(), locale) == 0); Assert::IsTrue(c == control.cend()); Assert::IsTrue(l == langs.cend()); } diff --git a/UnitTests/pool.cpp b/UnitTests/pool.cpp index 1b135d22d..e59dba2d6 100644 --- a/UnitTests/pool.cpp +++ b/UnitTests/pool.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -12,7 +12,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace UnitTests { - constexpr size_t capacity = 50; + constexpr size_t pool_capacity = 50; TEST_CLASS(pool) { @@ -24,15 +24,15 @@ namespace UnitTests pool_t pool; list workers; for (auto n = thread::hardware_concurrency(); n--; ) { - workers.push_back(std::move(thread([](_Inout_ pool_t& pool) - { - for (size_t n = 10000; n--; ) { - worker_t el = move(pool.pop()); - if (!el) - el.reset(new int(1)); - pool.push(move(el)); - } - }, ref(pool)))); + workers.push_back(thread([](_Inout_ pool_t& pool) + { + for (size_t n = 10000; n--; ) { + worker_t el = pool.pop(); + if (!el) + el.reset(new int(1)); + pool.push(std::move(el)); + } + }, ref(pool))); } for (auto& w : workers) diff --git a/UnitTests/ring.cpp b/UnitTests/ring.cpp index 4e04712fd..35dca3c6f 100644 --- a/UnitTests/ring.cpp +++ b/UnitTests/ring.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -12,20 +12,20 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace UnitTests { - constexpr size_t capacity = 50; + constexpr size_t ring_capacity = 50; TEST_CLASS(ring) { public: TEST_METHOD(test) { - using ring_t = stdex::ring; + using ring_t = stdex::ring; ring_t ring; thread writer([](_Inout_ ring_t& ring) { int seed = 0; for (size_t retries = 1000; retries--;) { - for (auto to_write = static_cast(static_cast(::rand()) * capacity / 5 / RAND_MAX); to_write;) { + for (auto to_write = static_cast(static_cast(::rand()) * ring_capacity / 5 / RAND_MAX); to_write;) { int* ptr; size_t num_write; tie(ptr, num_write) = ring.back(); if (to_write < num_write) diff --git a/UnitTests/stream.cpp b/UnitTests/stream.cpp index 21f029413..26690b2c3 100644 --- a/UnitTests/stream.cpp +++ b/UnitTests/stream.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -6,8 +6,6 @@ #include "pch.hpp" using namespace std; -using namespace stdex; -using namespace stdex::stream; #ifdef _WIN32 using namespace Microsoft::VisualStudio::CppUnitTestFramework; #endif @@ -20,9 +18,9 @@ namespace UnitTests TEST_METHOD(async) { constexpr uint32_t total = 1000; - memory_file source(mul(total, sizeof(uint32_t))); + stdex::stream::memory_file source(stdex::mul(total, sizeof(uint32_t))); { - async_writer<70> writer(source); + stdex::stream::async_writer<70> writer(source); for (uint32_t i = 0; i < total; ++i) { Assert::IsTrue(writer.ok()); writer << i; @@ -30,7 +28,7 @@ namespace UnitTests } Assert::AreEqual(0, source.seekbeg(0)); { - async_reader<50> reader(source); + stdex::stream::async_reader<50> reader(source); uint32_t x; for (uint32_t i = 0; i < total; ++i) { reader >> x; @@ -46,24 +44,24 @@ namespace UnitTests { constexpr uint32_t total = 1000; - memory_file f1(mul(total, sizeof(uint32_t))); + stdex::stream::memory_file f1(stdex::mul(total, sizeof(uint32_t))); - sstring filename2, filename3; + stdex::sstring filename2, filename3; filename2 = filename3 = temp_path(); filename2 += _T("stdex-stream-replicator-2.tmp"); - file f2( + stdex::stream::file f2( filename2.c_str(), - mode_for_reading | mode_for_writing | mode_create | mode_binary); + stdex::stream::mode_for_reading | stdex::stream::mode_for_writing | stdex::stream::mode_create | stdex::stream::mode_binary); filename3 += _T("stdex-stream-replicator-3.tmp"); - cached_file f3( + stdex::stream::cached_file f3( filename3.c_str(), - mode_for_reading | mode_for_writing | mode_create | mode_binary, + stdex::stream::mode_for_reading | stdex::stream::mode_for_writing | stdex::stream::mode_create | stdex::stream::mode_binary, 128); { stdex::stream::replicator writer; - buffer f2_buf(f2, 0, 32); + stdex::stream::buffer f2_buf(f2, 0, 32); writer.push_back(&f1); writer.push_back(&f2_buf); writer.push_back(&f3); @@ -77,7 +75,7 @@ namespace UnitTests f2.seekbeg(0); f3.seekbeg(0); { - buffer f2_buf(f2, 64, 0); + stdex::stream::buffer f2_buf(f2, 64, 0); uint32_t x; for (uint32_t i = 0; i < total; ++i) { f1 >> x; @@ -106,17 +104,17 @@ namespace UnitTests TEST_METHOD(open_close) { - cached_file dat(invalid_handle, state_t::fail, 4096); - const sstring filepath = temp_path(); + stdex::stream::cached_file dat(stdex::invalid_handle, stdex::stream::state_t::fail, 4096); + const stdex::sstring filepath = temp_path(); constexpr uint32_t count = 3; - sstring filename[count]; + stdex::sstring filename[count]; stdex::stream::fpos_t start[count]; for (uint32_t i = 0; i < count; ++i) { - filename[i] = filepath + sprintf(_T("stdex-stream-open_close%u.tmp"), NULL, i); - dat.open(filename[i].c_str(), mode_for_reading | mode_for_writing | share_none | mode_preserve_existing | mode_binary); + filename[i] = filepath + stdex::sprintf(_T("stdex-stream-open_close%u.tmp"), NULL, i); + dat.open(filename[i].c_str(), stdex::stream::mode_for_reading | stdex::stream::mode_for_writing | stdex::stream::share_none | stdex::stream::mode_preserve_existing | stdex::stream::mode_binary); Assert::IsTrue(dat.ok()); start[i] = dat.tell(); - Assert::AreNotEqual(fpos_max, start[i]); + Assert::AreNotEqual(stdex::stream::fpos_max, start[i]); for (uint32_t j = 0; j < 31 + 11 * i; ++j) { dat << j * count + i; Assert::IsTrue(dat.ok()); @@ -124,7 +122,7 @@ namespace UnitTests dat.close(); } for (uint32_t i = 0; i < count; ++i) { - dat.open(filename[i].c_str(), mode_for_reading | mode_open_existing | share_none | mode_binary); + dat.open(filename[i].c_str(), stdex::stream::mode_for_reading | stdex::stream::mode_open_existing | stdex::stream::share_none | stdex::stream::mode_binary); Assert::IsTrue(dat.ok()); for (;;) { uint32_t x; @@ -141,13 +139,13 @@ namespace UnitTests TEST_METHOD(file_stat) { - sstring path(temp_path()); + stdex::sstring path(temp_path()); Assert::IsTrue(stdex::stream::file::exists(path)); Assert::IsFalse(stdex::stream::file::readonly(path)); } protected: - static sstring temp_path() + static stdex::sstring temp_path() { #ifdef _WIN32 TCHAR temp_path[MAX_PATH]; diff --git a/UnitTests/string.cpp b/UnitTests/string.cpp index 671a0913f..10680c94a 100644 --- a/UnitTests/string.cpp +++ b/UnitTests/string.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2023-2024 Amebis */ @@ -17,19 +17,23 @@ namespace UnitTests public: TEST_METHOD(sprintf) { - Assert::AreEqual(L"This is a test.", stdex::sprintf(L"This is %s.", stdex::locale_default, L"a test").c_str()); - Assert::AreEqual(15, stdex::sprintf(L"This is %s.", stdex::locale_default, L"a test").size()); + Assert::AreEqual(L"This is a test.", stdex::sprintf(L"This is %ls.", stdex::locale_default, L"a test").c_str()); + Assert::AreEqual(15, stdex::sprintf(L"This is %ls.", stdex::locale_default, L"a test").size()); Assert::AreEqual("This is a test.", stdex::sprintf("This is %s.", stdex::locale_default, "a test").c_str()); Assert::AreEqual(15, stdex::sprintf("This is %s.", stdex::locale_default, "a test").size()); + // swprintf functions return EILSEQ when %ls inserts contain emoji on Mac. 😢 + Assert::AreEqual(L"This is a tést.", stdex::sprintf(L"This is %ls.", stdex::locale_default, L"a tést").c_str()); + Assert::AreEqual("This is a 🐔Test🐮.", stdex::sprintf("This is %s.", stdex::locale_default, "a 🐔Test🐮").c_str()); + wstring wstr; std::string str; for (size_t i = 0; i < 2000; i++) { - wstr += L"🐔Test🐮\r\n"; + wstr += L"tést\r\n"; str += "🐔Test🐮\r\n"; } - Assert::AreEqual(wstr.c_str(), stdex::sprintf(L"%s", stdex::locale_default, wstr.data()).c_str()); - Assert::AreEqual(wstr.size(), stdex::sprintf(L"%s", stdex::locale_default, wstr.data()).size()); + Assert::AreEqual(wstr.c_str(), stdex::sprintf(L"%ls", stdex::locale_default, wstr.data()).c_str()); + Assert::AreEqual(wstr.size(), stdex::sprintf(L"%ls", stdex::locale_default, wstr.data()).size()); Assert::AreEqual(str.c_str(), stdex::sprintf("%s", stdex::locale_utf8, str.data()).c_str()); Assert::AreEqual(str.size(), stdex::sprintf("%s", stdex::locale_utf8, str.data()).size()); } diff --git a/UnitTests/unicode.cpp b/UnitTests/unicode.cpp index b1c4a5618..a0200ad7b 100644 --- a/UnitTests/unicode.cpp +++ b/UnitTests/unicode.cpp @@ -8,7 +8,8 @@ using namespace std; #ifdef _WIN32 using namespace Microsoft::VisualStudio::CppUnitTestFramework; -#else +#endif +#ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -102,6 +103,6 @@ namespace UnitTests }; } -#ifndef _WIN32 +#ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/include/stdex/html.hpp b/include/stdex/html.hpp index 4f21ccc5e..4938d71a6 100644 --- a/include/stdex/html.hpp +++ b/include/stdex/html.hpp @@ -1,4 +1,4 @@ -/* +/* SPDX-License-Identifier: MIT Copyright © 2016-2024 Amebis */ @@ -820,8 +820,9 @@ namespace stdex case element_t::big: case element_t::small: return true; + default: + return false; }; - return false; } /// @@ -844,8 +845,9 @@ namespace stdex case element_t::acronym: case element_t::xmp: return true; + default: + return false; }; - return false; } /// @@ -877,8 +879,9 @@ namespace stdex case element_t::iframe: case element_t::nobr: return true; + default: + return false; }; - return false; } /// @@ -895,8 +898,9 @@ namespace stdex case element_t::label: case element_t::button: return true; + default: + return false; }; - return false; } /// @@ -929,8 +933,9 @@ namespace stdex case element_t::h5: case element_t::h6: return true; + default: + return false; }; - return false; } /// @@ -946,8 +951,9 @@ namespace stdex case element_t::dir: case element_t::menu: return true; + default: + return false; }; - return false; } /// @@ -961,8 +967,9 @@ namespace stdex case element_t::pre: case element_t::listing: return true; + default: + return false; } - return false; } /// @@ -992,8 +999,9 @@ namespace stdex case element_t::fieldset: case element_t::address: return true; + default: + return false; }; - return false; } /// @@ -1019,8 +1027,9 @@ namespace stdex case element_t::base: case element_t::nextid: return true; + default: + return false; }; - return false; } /// @@ -1037,12 +1046,13 @@ namespace stdex case element_t::link: case element_t::object: return true; + default: + return false; }; - return false; } /// - /// May element be a part of
?
+			/// May element be a part of `
`?
 			///
 			/// \param[in] code  Element code
 			///
@@ -1062,8 +1072,9 @@ namespace stdex
 				case element_t::basefont:
 				case element_t::nobr:
 					return true;
+				default:
+					return false;
 				};
-				return false;
 			}
 
 			///
@@ -1078,8 +1089,9 @@ namespace stdex
 				case element_t::body:
 				case element_t::frameset:
 					return true;
+				default:
+					return false;
 				};
-				return false;
 			}
 
 			///
@@ -1105,8 +1117,9 @@ namespace stdex
 				case element_t::th:
 				case element_t::tr:
 					return true;
+				default:
+					return false;
 				};
-				return false;
 			}
 
 			///
@@ -1205,8 +1218,8 @@ namespace stdex
 				case element_t::ul:            return child == element_t::li;
 				case element_t::wbr:           return false;
 				case element_t::unknown:       return true;
+				default:                       return false;
 				}
-				return false;
 			}
 
 			///
@@ -1260,8 +1273,8 @@ namespace stdex
 				case element_t::table:      return !stdex::strnicmp(attr_name, num_chars, "background", SIZE_MAX);
 				case element_t::td:         return !stdex::strnicmp(attr_name, num_chars, "background", SIZE_MAX);
 				case element_t::th:         return !stdex::strnicmp(attr_name, num_chars, "background", SIZE_MAX);
+				default:                    return false;
 				}
-				return false;
 			}
 
 			///
@@ -1286,8 +1299,8 @@ namespace stdex
 				case element_t::table:  return !stdex::strnicmp(attr_name, num_chars, "summary", SIZE_MAX);
 				case element_t::td:     return !stdex::strnicmp(attr_name, num_chars, "abbr", SIZE_MAX);
 				case element_t::th:     return !stdex::strnicmp(attr_name, num_chars, "abbr", SIZE_MAX);
+				default:                return false;
 				}
-				return false;
 			}
 		};
 
@@ -1754,6 +1767,7 @@ namespace stdex
 									case element_t::style:
 										m_is_special_element = true;
 										break;
+									default:;
 									}
 								}
 							}
@@ -1793,7 +1807,7 @@ namespace stdex
 								auto starting_tag = m_element_stack[j];
 								_Assume_(starting_tag && starting_tag->type == stdex::parser::html_sequence_t::element_start);
 								if (starting_tag->code == e->code ||
-									starting_tag->code == element_t::unknown && e->code == element_t::unknown && !stdex::strnicmp(source + starting_tag->name.start, starting_tag->name.size(), source + e->name.start, e->name.size()))
+									(starting_tag->code == element_t::unknown && e->code == element_t::unknown && !stdex::strnicmp(source + starting_tag->name.start, starting_tag->name.size(), source + e->name.start, e->name.size())))
 								{
 									e->start = starting_tag;
 									e->parent = starting_tag->parent;
@@ -2098,7 +2112,7 @@ namespace stdex
 				_In_opt_ stdex::html::sequence* sequence = nullptr,
 				_In_opt_ stdex::html::sequence* _end_sequence = nullptr,
 				_In_ uintptr_t data = 0) :
-				text_token(token_t::starting, _text, num_chars_text, text_type, sequence, data),
+				text_token(token_t::starting, _text, num_chars_text, text_type, sequence, data),
 				name(_name, num_chars_name),
 				end_sequence(_end_sequence)
 			{}
@@ -2621,8 +2635,8 @@ namespace stdex
 						start = m_css_cdc.interval.end;
 					}
 					else if (
-						m_css_import.match(m_source, start, end) && (section = m_css_import.interval, content = m_css_import.content, true) ||
-						m_css_uri.match(m_source, start, end) && (section = m_css_uri.interval, content = m_css_uri.content, true))
+						(m_css_import.match(m_source, start, end) && ((void)(section = m_css_import.interval), (void)(content = m_css_import.content), true)) ||
+						(m_css_uri.match(m_source, start, end) && ((void)(section = m_css_uri.interval), (void)(content = m_css_uri.content), true)))
 					{
 						std::unique_ptr> t_url(
 							new url_token(
diff --git a/include/stdex/locale.hpp b/include/stdex/locale.hpp
index 3186f366f..b15e335f7 100644
--- a/include/stdex/locale.hpp
+++ b/include/stdex/locale.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2016-2024 Amebis
 */
@@ -75,12 +75,14 @@ namespace stdex
 		{}
 
 		locale(_In_ int category, _In_z_ const char* locale) :
-			locale(create_locale(category, locale))
+			stdex::locale(create_locale(category, locale))
 		{}
 
+#ifdef _WIN32
 		locale(_In_ int category, _In_z_ const wchar_t* locale) :
-			locale(create_locale(category, locale))
+			stdex::locale(create_locale(category, locale))
 		{}
+#endif
 
 		operator locale_t() const { return get(); }
 	};
@@ -109,4 +111,4 @@ namespace stdex
 		""
 #endif
 		));
-}
\ No newline at end of file
+}
diff --git a/include/stdex/parser.hpp b/include/stdex/parser.hpp
index b9b2ecf11..e9d1eed08 100644
--- a/include/stdex/parser.hpp
+++ b/include/stdex/parser.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -7371,7 +7371,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->content.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval content; ///< content position in source
@@ -7506,7 +7506,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->content.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval content; ///< content position in source
@@ -7573,7 +7573,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->content.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval content; ///< content position in source
@@ -7598,7 +7598,7 @@ namespace stdex
 					this->interval.end = this->interval.end + 4;
 
 					// Skip whitespace.
-					const auto& ctype = std::use_facet>(m_locale);
+					const auto& ctype = std::use_facet>(this->m_locale);
 					for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
 
 					if (this->interval.end < end &&
@@ -7683,7 +7683,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->content.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval content; ///< content position in source
@@ -7711,7 +7711,7 @@ namespace stdex
 					this->interval.end = this->interval.end + 7;
 
 					// Skip whitespace.
-					const auto& ctype = std::use_facet>(m_locale);
+					const auto& ctype = std::use_facet>(this->m_locale);
 					for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
 
 					if (this->interval.end < end &&
@@ -7769,7 +7769,7 @@ namespace stdex
 				this->base_type.invalidate();
 				this->sub_type.invalidate();
 				this->charset.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval base_type; ///< basic type position in source
@@ -7784,7 +7784,7 @@ namespace stdex
 				_In_ int flags = match_multiline)
 			{
 				_Unreferenced_(flags);
-				const auto& ctype = std::use_facet>(m_locale);
+				const auto& ctype = std::use_facet>(this->m_locale);
 
 				this->interval.end = start;
 				this->base_type.start = this->interval.end;
@@ -7904,7 +7904,7 @@ namespace stdex
 				_In_ int flags = match_default)
 			{
 				_Unreferenced_(flags);
-				const auto& ctype = std::use_facet>(m_locale);
+				const auto& ctype = std::use_facet>(this->m_locale);
 				this->interval.end = start;
 				for (;;) {
 					_Assume_(text || this->interval.end >= end);
@@ -7918,7 +7918,7 @@ namespace stdex
 					}
 					if (text[this->interval.end] == '>' ||
 						text[this->interval.end] == '=' ||
-						text[this->interval.end] == '/' && this->interval.end + 1 < end && text[this->interval.end + 1] == '>' ||
+						(text[this->interval.end] == '/' && this->interval.end + 1 < end && text[this->interval.end + 1] == '>') ||
 						ctype.is(ctype.space, text[this->interval.end]))
 					{
 						this->interval.start = start;
@@ -7947,7 +7947,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->content.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval content; ///< content position in source
@@ -7988,7 +7988,7 @@ namespace stdex
 
 				// Nonquoted
 				this->content.start = this->interval.end;
-				const auto& ctype = std::use_facet>(m_locale);
+				const auto& ctype = std::use_facet>(this->m_locale);
 				for (;;) {
 					_Assume_(text || this->interval.end >= end);
 					if (this->interval.end >= end || !text[this->interval.end]) {
@@ -7997,7 +7997,7 @@ namespace stdex
 						return true;
 					}
 					if (text[this->interval.end] == '>' ||
-						text[this->interval.end] == '/' && this->interval.end + 1 < end && text[this->interval.end + 1] == '>' ||
+						(text[this->interval.end] == '/' && this->interval.end + 1 < end && text[this->interval.end + 1] == '>') ||
 						ctype.is(ctype.space, text[this->interval.end]))
 					{
 						this->content.end = this->interval.end;
@@ -8050,7 +8050,7 @@ namespace stdex
 		{
 		public:
 			basic_html_tag(_In_ const std::locale& locale = std::locale()) :
-				basic_parser(locale),
+				basic_parser(locale),
 				type(html_sequence_t::unknown)
 			{}
 
@@ -8059,7 +8059,7 @@ namespace stdex
 				this->type = html_sequence_t::unknown;
 				this->name.invalidate();
 				this->attributes.clear();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			html_sequence_t type;                   ///< tag type
@@ -8157,102 +8157,104 @@ namespace stdex
 				else
 					goto error;
 
-				// Skip whitespace.
-				const auto& ctype = std::use_facet>(m_locale);
-				for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
-
-				this->attributes.clear();
-				for (;;) {
-					if (this->type == html_sequence_t::element_start &&
-						this->interval.end + 1 < end &&
-						text[this->interval.end] == '/' &&
-						text[this->interval.end + 1] == '>')
-					{
-						// 
-						this->type = html_sequence_t::element;
-						this->interval.end = this->interval.end + 2;
-						break;
-					}
-					if (this->interval.end < end &&
-						text[this->interval.end] == '>')
-					{
-						// 
-						this->interval.end++;
-						break;
-					}
-					if (this->type == html_sequence_t::declaration &&
-						this->interval.end + 1 < end &&
-						text[this->interval.end] == '!' &&
-						text[this->interval.end + 1] == '>')
-					{
-						// "".
-						this->interval.end = this->interval.end + 2;
-						break;
-					}
-					if (this->type == html_sequence_t::declaration &&
-						this->interval.end + 1 < end &&
-						text[this->interval.end] == '-' &&
-						text[this->interval.end + 1] == '-')
-					{
-						// "interval.end = this->interval.end + 2;
-						for (;;) {
-							if (this->interval.end >= end || !text[this->interval.end])
-								goto error;
-							if (this->interval.end + 1 < end &&
-								text[this->interval.end] == '-' &&
-								text[this->interval.end + 1] == '-')
-							{
-								// "interval.end = this->interval.end + 2;
-								break;
-							}
-							this->interval.end++;
-						}
-
-						// Skip whitespace.
-						for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
-						continue;
-					}
-
-					if (this->interval.end >= end || !text[this->interval.end])
-						goto error;
-
-					// Attributes follow...
-					html_attribute* a = nullptr;
-					if (this->m_ident.match(text, this->interval.end, end, flags)) {
-						this->attributes.push_back(std::move(html_attribute{ this->m_ident.interval }));
-						a = &this->attributes.back();
-						_Assume_(a);
-						this->interval.end = this->m_ident.interval.end;
-					}
-					else {
-						// What was that?! Skip.
-						this->interval.end++;
-						continue;
-					}
-
+				{
 					// Skip whitespace.
+					const auto& ctype = std::use_facet>(this->m_locale);
 					for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
 
-					if (this->interval.end < end && text[this->interval.end] == '=') {
-						this->interval.end++;
-
-						// Skip whitespace.
-						for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
-
-						if (this->m_value.match(text, this->interval.end, end, flags)) {
-							// This attribute has value.
-							a->value = this->m_value.content;
-							this->interval.end = this->m_value.interval.end;
+					this->attributes.clear();
+					for (;;) {
+						if (this->type == html_sequence_t::element_start &&
+							this->interval.end + 1 < end &&
+							text[this->interval.end] == '/' &&
+							text[this->interval.end + 1] == '>')
+						{
+							// 
+							this->type = html_sequence_t::element;
+							this->interval.end = this->interval.end + 2;
+							break;
+						}
+						if (this->interval.end < end &&
+							text[this->interval.end] == '>')
+						{
+							// 
+							this->interval.end++;
+							break;
+						}
+						if (this->type == html_sequence_t::declaration &&
+							this->interval.end + 1 < end &&
+							text[this->interval.end] == '!' &&
+							text[this->interval.end + 1] == '>')
+						{
+							// "".
+							this->interval.end = this->interval.end + 2;
+							break;
+						}
+						if (this->type == html_sequence_t::declaration &&
+							this->interval.end + 1 < end &&
+							text[this->interval.end] == '-' &&
+							text[this->interval.end + 1] == '-')
+						{
+							// "interval.end = this->interval.end + 2;
+							for (;;) {
+								if (this->interval.end >= end || !text[this->interval.end])
+									goto error;
+								if (this->interval.end + 1 < end &&
+									text[this->interval.end] == '-' &&
+									text[this->interval.end + 1] == '-')
+								{
+									// "interval.end = this->interval.end + 2;
+									break;
+								}
+								this->interval.end++;
+							}
 
 							// Skip whitespace.
 							for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
+							continue;
+						}
+
+						if (this->interval.end >= end || !text[this->interval.end])
+							goto error;
+
+						// Attributes follow...
+						html_attribute* a = nullptr;
+						if (this->m_ident.match(text, this->interval.end, end, flags)) {
+							this->attributes.push_back(std::move(html_attribute{ this->m_ident.interval }));
+							a = &this->attributes.back();
+							_Assume_(a);
+							this->interval.end = this->m_ident.interval.end;
+						}
+						else {
+							// What was that?! Skip.
+							this->interval.end++;
+							continue;
+						}
+
+						// Skip whitespace.
+						for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
+
+						if (this->interval.end < end && text[this->interval.end] == '=') {
+							this->interval.end++;
+
+							// Skip whitespace.
+							for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
+
+							if (this->m_value.match(text, this->interval.end, end, flags)) {
+								// This attribute has value.
+								a->value = this->m_value.content;
+								this->interval.end = this->m_value.interval.end;
+
+								// Skip whitespace.
+								for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
+							}
+						}
+						else {
+							// This attribute has no value.
+							a->value.invalidate();
 						}
-					}
-					else {
-						// This attribute has no value.
-						a->value.invalidate();
 					}
 				}
 
@@ -8286,7 +8288,7 @@ namespace stdex
 			virtual void invalidate()
 			{
 				this->condition.invalidate();
-				basic_parser::invalidate();
+				basic_parser::invalidate();
 			}
 
 			stdex::interval condition; /// condition position in source
@@ -8308,7 +8310,7 @@ namespace stdex
 					this->interval.end = start + 3;
 
 					// Skip whitespace.
-					const auto& ctype = std::use_facet>(m_locale);
+					const auto& ctype = std::use_facet>(this->m_locale);
 					for (; this->interval.end < end && text[this->interval.end] && ctype.is(ctype.space, text[this->interval.end]); this->interval.end++);
 
 					this->condition.start = this->condition.end = this->interval.end;
diff --git a/include/stdex/pool.hpp b/include/stdex/pool.hpp
index 59ed7dfe6..dde45c667 100644
--- a/include/stdex/pool.hpp
+++ b/include/stdex/pool.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -7,7 +7,9 @@
 
 #include "compat.hpp"
 #include "spinlock.hpp"
+#ifdef _WIN32
 #include "windows.h"
+#endif
 #include 
 #include 
 #include 
@@ -39,11 +41,13 @@ namespace stdex
 	private:
 		static numaid_t numa_node()
 		{
-#ifdef _WIN32
+#if defined(_WIN32)
 			PROCESSOR_NUMBER Processor;
 			GetCurrentProcessorNumberEx(&Processor);
 			USHORT NodeNumber = 0;
 			return GetNumaProcessorNodeEx(&Processor, &NodeNumber) ? NodeNumber : 0;
+#elif defined(__APPLE__)
+			return 0;
 #else
 			return numa_node_of_cpu(sched_getcpu());
 #endif
@@ -101,4 +105,4 @@ namespace stdex
 			}
 		}
 	};
-}
\ No newline at end of file
+}
diff --git a/include/stdex/progress.hpp b/include/stdex/progress.hpp
index fdd14d46c..267e2be0c 100644
--- a/include/stdex/progress.hpp
+++ b/include/stdex/progress.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -207,7 +207,7 @@ namespace stdex
 		virtual bool cancel()
 		{
 			return
-				m_host && m_host->cancel() ||
+				(m_host && m_host->cancel()) ||
 				m_deadline < std::chrono::high_resolution_clock::now();
 		}
 
diff --git a/include/stdex/spinlock.hpp b/include/stdex/spinlock.hpp
index 1cbded82e..60570f1db 100644
--- a/include/stdex/spinlock.hpp
+++ b/include/stdex/spinlock.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -43,7 +43,7 @@ namespace stdex
 #elif _M_IX86 || _M_X64
 					_mm_pause();
 #elif __aarch64__
-					__yield();
+					asm volatile("yield");
 #elif __i386__ || __x86_64__
 					__builtin_ia32_pause();
 #endif
diff --git a/include/stdex/stream.hpp b/include/stdex/stream.hpp
index bd3453867..29734df49 100644
--- a/include/stdex/stream.hpp
+++ b/include/stdex/stream.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -2408,8 +2408,8 @@ namespace stdex
 				_Assume_(data || !length);
 				constexpr int block_size = 0x10000000;
 				for (size_t to_read = length;;) {
-					int num_read = recv(m_h, reinterpret_cast(data), static_cast(std::min(to_read, block_size)), 0);
-					if (num_read == SOCKET_ERROR) _Unlikely_ {
+					auto num_read = recv(m_h, reinterpret_cast(data), static_cast(std::min(to_read, block_size)), 0);
+					if (num_read == -1) _Unlikely_ {
 						m_state = to_read < length ? state_t::ok : state_t::fail;
 						return length - to_read;
 					}
@@ -2432,8 +2432,8 @@ namespace stdex
 				_Assume_(data || !length);
 				constexpr int block_size = 0x10000000;
 				for (size_t to_write = length;;) {
-					int num_written = send(m_h, reinterpret_cast(data), static_cast(std::min(to_write, block_size)), 0);
-					if (num_written == SOCKET_ERROR) _Unlikely_ {
+					auto num_written = send(m_h, reinterpret_cast(data), static_cast(std::min(to_write, block_size)), 0);
+					if (num_written == -1) _Unlikely_ {
 						m_state = state_t::fail;
 						return length - to_write;
 					}
diff --git a/include/stdex/string.hpp b/include/stdex/string.hpp
index b8a47c37c..986f7365a 100644
--- a/include/stdex/string.hpp
+++ b/include/stdex/string.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2016-2024 Amebis
 */
@@ -2328,7 +2328,7 @@ namespace stdex
 #pragma warning(suppress: 4996)
 		return _vsnprintf_l(str, capacity, format, locale, arg);
 #else
-		return ::vsnprintf(str, capacity, format, arg);
+		return ::vsnprintf_l(str, capacity, locale, format, arg);
 #endif
 	}
 
@@ -2338,7 +2338,7 @@ namespace stdex
 #pragma warning(suppress: 4996)
 		return _vsnwprintf_l(str, capacity, format, locale, arg);
 #else
-		return vswprintf(str, capacity, format, arg);
+		return ::vswprintf_l(str, capacity, locale, format, arg);
 #endif
 	}
 	/// \endcond
@@ -2360,11 +2360,12 @@ namespace stdex
 
 		// Try with stack buffer first.
 		int count = vsnprintf(buf, _countof(buf), format, locale, arg);
-		if (0 <= count && count < _countof(buf)) {
+		if (0 <= count && count <= _countof(buf)) {
 			// Copy from stack.
 			str.append(buf, count);
 			return count;
 		}
+#ifdef _WIN32
 		if (count < 0) {
 			switch (errno) {
 			case 0:
@@ -2380,6 +2381,25 @@ namespace stdex
 		str.resize(offset + count);
 		if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
 			throw std::runtime_error("failed to format string");
+#else
+		size_t offset = str.size();
+		for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
+			switch (errno) {
+			case EOVERFLOW:
+				// Allocate on heap and retry.
+				str.resize(offset + capacity);
+				count = vsnprintf(&str[offset], capacity, format, locale, arg);
+				if (0 <= count && count <= capacity) {
+					str.resize(offset + count);
+					return count;
+				}
+				break;
+			case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
+			case EILSEQ: throw std::runtime_error("encoding error");
+			default: throw std::runtime_error("failed to format string");
+			}
+		}
+#endif
 		return count;
 	}
 
@@ -2538,7 +2558,6 @@ namespace stdex
 	///
 	/// Formats a time string using `strftime()`.
 	///
-	/// \param[out] str     String to append formatted time text
 	/// \param[in ] format  String template using `strftime()` style
 	/// \param[in ] time    Time
 	/// \param[in ] locale  Stdlib locale used to perform formatting. Use `NULL` to use locale globally set by `setlocale()`.
@@ -2619,7 +2638,7 @@ namespace stdex
 	template
 	inline void strlwr(_Inout_ T (&str)[N])
 	{
-		strlwr(str, count);
+		strlwr(str, N);
 	}
 
 	///
@@ -2631,7 +2650,7 @@ namespace stdex
 	template
 	inline void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
 	{
-		strlwr(str, count, locale);
+		strlwr(str, N, locale);
 	}
 
 	///
diff --git a/include/stdex/sys_info.hpp b/include/stdex/sys_info.hpp
index 85b5dd8ef..02b8e6733 100644
--- a/include/stdex/sys_info.hpp
+++ b/include/stdex/sys_info.hpp
@@ -1,4 +1,4 @@
-/*
+/*
 	SPDX-License-Identifier: MIT
 	Copyright © 2023-2024 Amebis
 */
@@ -7,7 +7,7 @@
 
 #include "compat.hpp"
 #include "system.hpp"
-#ifdef _WIN32
+#if defined(_WIN32)
 #include "windows.h"
 #include 
 #include 
@@ -35,12 +35,12 @@ constexpr stdex::platform_id IMAGE_FILE_MACHINE_AMD64 = "x86_64";
 constexpr stdex::platform_id IMAGE_FILE_MACHINE_ARMNT = "arm";
 constexpr stdex::platform_id IMAGE_FILE_MACHINE_ARM64 = "aarch64";
 
-inline bool operator ==(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a == b; }
-inline bool operator !=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a != b; }
-inline bool operator <(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a == IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN || a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) < 0; }
-inline bool operator <=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a == IMAGE_FILE_MACHINE_UNKNOWN || a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) <= 0; }
-inline bool operator >(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a != IMAGE_FILE_MACHINE_UNKNOWN && b == IMAGE_FILE_MACHINE_UNKNOWN || a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) > 0; }
-inline bool operator >=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return b == IMAGE_FILE_MACHINE_UNKNOWN || a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) >= 0; }
+//inline bool operator ==(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a == b; }
+//inline bool operator !=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a != b; }
+//inline bool operator <(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return (a == IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN) || (a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) < 0); }
+//inline bool operator <=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return a == IMAGE_FILE_MACHINE_UNKNOWN || (a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) <= 0); }
+//inline bool operator >(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return (a != IMAGE_FILE_MACHINE_UNKNOWN && b == IMAGE_FILE_MACHINE_UNKNOWN) || (a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) > 0); }
+//inline bool operator >=(_In_ const stdex::platform_id a, _In_ const stdex::platform_id b) { return b == IMAGE_FILE_MACHINE_UNKNOWN || (a != IMAGE_FILE_MACHINE_UNKNOWN && b != IMAGE_FILE_MACHINE_UNKNOWN && strcmp(a, b) >= 0); }
 #endif
 
 namespace stdex
@@ -189,8 +189,8 @@ namespace stdex
 			}
 #elif defined(__APPLE__)
 			{
-				gid_t gids[NGROUPS + 1]; // A user cannot be member in more than NGROUPS groups, not counting the default group (hence the + 1)
-				for (int i = 0, n = getgroups(_countof(gids), gids); i < n; ++i) {
+				gid_t gids[NGROUPS_MAX];
+				for (int i = 0, n = getgroups(NGROUPS_MAX, gids); i < n; ++i) {
 					struct group* group = getgrgid(gids[i]);
 					if (!group) continue;
 					if (strcmp(group->gr_name, "admin") == 0) {
diff --git a/include/stdex/system.hpp b/include/stdex/system.hpp
index ac66a0f33..890036053 100644
--- a/include/stdex/system.hpp
+++ b/include/stdex/system.hpp
@@ -6,7 +6,7 @@
 #pragma once
 
 #include "compat.hpp"
-#ifdef _WIN32
+#if defined(_WIN32)
 #include "windows.h"
 #include 
 #include 
diff --git a/include/stdex/unicode.hpp b/include/stdex/unicode.hpp
index e192a1f60..abd63e33f 100644
--- a/include/stdex/unicode.hpp
+++ b/include/stdex/unicode.hpp
@@ -18,7 +18,7 @@
 #include 
 #include 
 
-#ifndef _WIN32
+#ifdef __GNUC__
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -976,6 +976,6 @@ namespace stdex
 #endif
 }
 
-#ifndef _WIN32
+#ifdef __GNUC__
 #pragma GCC diagnostic pop
 #endif