diff --git a/Import/Vlpp.Linux.cpp b/Import/Vlpp.Linux.cpp new file mode 100644 index 00000000..1acaf3bc --- /dev/null +++ b/Import/Vlpp.Linux.cpp @@ -0,0 +1,224 @@ +/*********************************************************************** +THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY +DEVELOPER: Zihan Chen(vczh) +***********************************************************************/ +#include "Vlpp.h" + +/*********************************************************************** +.\CONSOLE.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + namespace console + { + +/*********************************************************************** +Console +***********************************************************************/ + + void Console::Write(const wchar_t* string, vint length) + { + std::wstring s(string, string + length); + std::wcout << s << std::ends; + } + + WString Console::Read() + { + std::wstring s; + std::getline(std::wcin, s, L'\n'); + return s.c_str(); + } + + void Console::SetColor(bool red, bool green, bool blue, bool light) + { + int color = (blue ? 1 : 0) * 4 + (green ? 1 : 0) * 2 + (red ? 1 : 0); + if (light) + wprintf(L"\x1B[00;3%dm", color); + else + wprintf(L"\x1B[01;3%dm", color); + } + + void Console::SetTitle(const WString& string) + { + } + } +} + + +/*********************************************************************** +.\PRIMITIVES\DATETIME.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include +#include + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + +/*********************************************************************** +DateTime +***********************************************************************/ + + DateTime ConvertTMToDateTime(tm* timeinfo, vint milliseconds) + { + time_t timer = mktime(timeinfo); + DateTime dt; + dt.year = timeinfo->tm_year + 1900; + dt.month = timeinfo->tm_mon + 1; + dt.day = timeinfo->tm_mday; + dt.dayOfWeek = timeinfo->tm_wday; + dt.hour = timeinfo->tm_hour; + dt.minute = timeinfo->tm_min; + dt.second = timeinfo->tm_sec; + dt.milliseconds = milliseconds; + + // in Linux and macOS, filetime will be mktime(t) * 1000 + gettimeofday().tv_usec / 1000 + dt.filetime = (vuint64_t)timer * 1000 + milliseconds; + dt.totalMilliseconds = (vuint64_t)timer * 1000 + milliseconds; + return dt; + } + + DateTime DateTime::LocalTime() + { + struct timeval tv; + gettimeofday(&tv, nullptr); + tm* timeinfo = localtime(&tv.tv_sec); + return ConvertTMToDateTime(timeinfo, tv.tv_usec / 1000); + } + + DateTime DateTime::UtcTime() + { + struct timeval tv; + gettimeofday(&tv, nullptr); + tm* timeinfo = gmtime(&tv.tv_sec); + return ConvertTMToDateTime(timeinfo, tv.tv_usec / 1000); + } + + DateTime DateTime::FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds) + { + tm timeinfo; + memset(&timeinfo, 0, sizeof(timeinfo)); + timeinfo.tm_year = _year - 1900; + timeinfo.tm_mon = _month - 1; + timeinfo.tm_mday = _day; + timeinfo.tm_hour = _hour; + timeinfo.tm_min = _minute; + timeinfo.tm_sec = _second; + timeinfo.tm_isdst = -1; + return ConvertTMToDateTime(&timeinfo, _milliseconds); + } + + DateTime DateTime::FromFileTime(vuint64_t filetime) + { + time_t timer = (time_t)(filetime / 1000); + tm* timeinfo = localtime(&timer); + return ConvertTMToDateTime(timeinfo, filetime % 1000); + } + + DateTime DateTime::ToLocalTime() + { + time_t localTimer = time(nullptr); + time_t utcTimer = mktime(gmtime(&localTimer)); + time_t timer = (time_t)(filetime / 1000) + localTimer - utcTimer; + tm* timeinfo = localtime(&timer); + return ConvertTMToDateTime(timeinfo, milliseconds); + } + + DateTime DateTime::ToUtcTime() + { + time_t timer = (time_t)(filetime / 1000); + tm* timeinfo = gmtime(&timer); + return ConvertTMToDateTime(timeinfo, milliseconds); + } + + DateTime DateTime::Forward(vuint64_t milliseconds) + { + return FromFileTime(filetime + milliseconds); + } + + DateTime DateTime::Backward(vuint64_t milliseconds) + { + return FromFileTime(filetime - milliseconds); + } +} + + +/*********************************************************************** +.\STRINGS\CONVERSION.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include +#include + +namespace vl +{ +/*********************************************************************** +String Conversions (buffer walkthrough) +***********************************************************************/ + + vint _wtoa(const wchar_t* w, char* a, vint chars) + { + return wcstombs(a, w, chars - 1) + 1; + } + + vint _atow(const char* a, wchar_t* w, vint chars) + { + return mbstowcs(w, a, chars - 1) + 1; + } +} + + +/*********************************************************************** +.\UNITTEST\UNITTEST.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + namespace unittest + { +/*********************************************************************** +UnitTest +***********************************************************************/ + + bool UnitTest::IsDebuggerAttached() + { + return false; + } + } +} + diff --git a/Import/Vlpp.Windows.cpp b/Import/Vlpp.Windows.cpp new file mode 100644 index 00000000..ba5e4f6f --- /dev/null +++ b/Import/Vlpp.Windows.cpp @@ -0,0 +1,282 @@ +/*********************************************************************** +THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY +DEVELOPER: Zihan Chen(vczh) +***********************************************************************/ +#include "Vlpp.h" + +/*********************************************************************** +.\CONSOLE.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + namespace console + { + +/*********************************************************************** +Console +***********************************************************************/ + + void Console::Write(const wchar_t* string, vint length) + { + HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD fileMode = 0; + DWORD written = 0; + if ((GetFileType(outHandle) & FILE_TYPE_CHAR) && GetConsoleMode(outHandle, &fileMode)) + { + WriteConsole(outHandle, string, (int)length, &written, 0); + } + else + { + int codePage = GetConsoleOutputCP(); + int charCount = WideCharToMultiByte(codePage, 0, string, -1, 0, 0, 0, 0); + char* codePageBuffer = new char[charCount]; + WideCharToMultiByte(codePage, 0, string, -1, codePageBuffer, charCount, 0, 0); + WriteFile(outHandle, codePageBuffer, charCount - 1, &written, 0); + delete[] codePageBuffer; + } + } + + WString Console::Read() + { + WString result; + DWORD count; + for (;;) + { + wchar_t buffer; + ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &buffer, 1, &count, 0); + if (buffer == L'\r') + { + ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &buffer, 1, &count, 0); + break; + } + else if (buffer == L'\n') + { + break; + } + else + { + result = result + WString::FromChar(buffer); + } + } + return result; + } + + void Console::SetColor(bool red, bool green, bool blue, bool light) + { + WORD attribute = 0; + if (red)attribute |= FOREGROUND_RED; + if (green)attribute |= FOREGROUND_GREEN; + if (blue)attribute |= FOREGROUND_BLUE; + if (light)attribute |= FOREGROUND_INTENSITY; + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attribute); + SetConsoleTextAttribute(GetStdHandle(STD_INPUT_HANDLE), attribute); + } + + void Console::SetTitle(const WString& string) + { + SetConsoleTitle(string.Buffer()); + } + } +} + + +/*********************************************************************** +.\PRIMITIVES\DATETIME.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + +/*********************************************************************** +DateTime +***********************************************************************/ + + DateTime SystemTimeToDateTime(const SYSTEMTIME& systemTime) + { + DateTime dateTime; + dateTime.year = systemTime.wYear; + dateTime.month = systemTime.wMonth; + dateTime.dayOfWeek = systemTime.wDayOfWeek; + dateTime.day = systemTime.wDay; + dateTime.hour = systemTime.wHour; + dateTime.minute = systemTime.wMinute; + dateTime.second = systemTime.wSecond; + dateTime.milliseconds = systemTime.wMilliseconds; + + FILETIME fileTime; + SystemTimeToFileTime(&systemTime, &fileTime); + ULARGE_INTEGER largeInteger; + largeInteger.HighPart = fileTime.dwHighDateTime; + largeInteger.LowPart = fileTime.dwLowDateTime; + dateTime.filetime = largeInteger.QuadPart; + dateTime.totalMilliseconds = dateTime.filetime / 10000; + + return dateTime; + } + + SYSTEMTIME DateTimeToSystemTime(const DateTime& dateTime) + { + ULARGE_INTEGER largeInteger; + largeInteger.QuadPart = dateTime.filetime; + FILETIME fileTime; + fileTime.dwHighDateTime = largeInteger.HighPart; + fileTime.dwLowDateTime = largeInteger.LowPart; + + SYSTEMTIME systemTime; + FileTimeToSystemTime(&fileTime, &systemTime); + return systemTime; + } + + DateTime DateTime::LocalTime() + { + SYSTEMTIME systemTime; + GetLocalTime(&systemTime); + return SystemTimeToDateTime(systemTime); + } + + DateTime DateTime::UtcTime() + { + SYSTEMTIME utcTime; + GetSystemTime(&utcTime); + return SystemTimeToDateTime(utcTime); + } + + DateTime DateTime::FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds) + { + SYSTEMTIME systemTime; + memset(&systemTime, 0, sizeof(systemTime)); + systemTime.wYear = (WORD)_year; + systemTime.wMonth = (WORD)_month; + systemTime.wDay = (WORD)_day; + systemTime.wHour = (WORD)_hour; + systemTime.wMinute = (WORD)_minute; + systemTime.wSecond = (WORD)_second; + systemTime.wMilliseconds = (WORD)_milliseconds; + + FILETIME fileTime; + SystemTimeToFileTime(&systemTime, &fileTime); + FileTimeToSystemTime(&fileTime, &systemTime); + return SystemTimeToDateTime(systemTime); + } + + DateTime DateTime::FromFileTime(vuint64_t filetime) + { + ULARGE_INTEGER largeInteger; + largeInteger.QuadPart = filetime; + FILETIME fileTime; + fileTime.dwHighDateTime = largeInteger.HighPart; + fileTime.dwLowDateTime = largeInteger.LowPart; + + SYSTEMTIME systemTime; + FileTimeToSystemTime(&fileTime, &systemTime); + return SystemTimeToDateTime(systemTime); + } + + DateTime DateTime::ToLocalTime() + { + SYSTEMTIME utcTime = DateTimeToSystemTime(*this); + SYSTEMTIME localTime; + SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime); + return SystemTimeToDateTime(localTime); + } + + DateTime DateTime::ToUtcTime() + { + SYSTEMTIME localTime = DateTimeToSystemTime(*this); + SYSTEMTIME utcTime; + TzSpecificLocalTimeToSystemTime(NULL, &localTime, &utcTime); + return SystemTimeToDateTime(utcTime); + } + + DateTime DateTime::Forward(vuint64_t milliseconds) + { + return FromFileTime(filetime + milliseconds * 10000); + } + + DateTime DateTime::Backward(vuint64_t milliseconds) + { + return FromFileTime(filetime - milliseconds * 10000); + } +} + + +/*********************************************************************** +.\STRINGS\CONVERSION.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ +/*********************************************************************** +String Conversions (buffer walkthrough) +***********************************************************************/ + + vint _wtoa(const wchar_t* w, char* a, vint chars) + { + return WideCharToMultiByte(CP_THREAD_ACP, 0, w, -1, a, (int)(a ? chars : 0), 0, 0); + } + + vint _atow(const char* a, wchar_t* w, vint chars) + { + return MultiByteToWideChar(CP_THREAD_ACP, 0, a, -1, w, (int)(w ? chars : 0)); + } +} + + +/*********************************************************************** +.\UNITTEST\UNITTEST.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + namespace unittest + { +/*********************************************************************** +UnitTest +***********************************************************************/ + + bool UnitTest::IsDebuggerAttached() + { + return IsDebuggerPresent(); + } + } +} + diff --git a/Import/Vlpp.cpp b/Import/Vlpp.cpp index b8c90ac9..915c51d3 100644 --- a/Import/Vlpp.cpp +++ b/Import/Vlpp.cpp @@ -40,13 +40,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#include -#elif defined VCZH_GCC -#include -#include -using namespace std; -#endif namespace vl { @@ -57,31 +50,6 @@ namespace vl Console ***********************************************************************/ - void Console::Write(const wchar_t* string, vint length) - { -#if defined VCZH_MSVC - HANDLE outHandle=GetStdHandle(STD_OUTPUT_HANDLE); - DWORD fileMode=0; - DWORD written=0; - if((GetFileType(outHandle) & FILE_TYPE_CHAR) && GetConsoleMode(outHandle, &fileMode)) - { - WriteConsole(outHandle, string, (int)length, &written,0); - } - else - { - int codePage = GetConsoleOutputCP(); - int charCount = WideCharToMultiByte(codePage, 0, string, -1, 0, 0, 0, 0); - char* codePageBuffer = new char[charCount]; - WideCharToMultiByte(codePage, 0, string, -1, codePageBuffer, charCount, 0, 0); - WriteFile(outHandle, codePageBuffer, charCount-1, &written, 0); - delete[] codePageBuffer; - } -#elif defined VCZH_GCC - wstring s(string, string+length); - wcout< -#if defined VCZH_MSVC -#elif defined VCZH_GCC -#include -#endif - -namespace vl -{ - -/*********************************************************************** -DateTime -***********************************************************************/ - -#if defined VCZH_MSVC - DateTime SystemTimeToDateTime(const SYSTEMTIME& systemTime) - { - DateTime dateTime; - dateTime.year=systemTime.wYear; - dateTime.month=systemTime.wMonth; - dateTime.dayOfWeek=systemTime.wDayOfWeek; - dateTime.day=systemTime.wDay; - dateTime.hour=systemTime.wHour; - dateTime.minute=systemTime.wMinute; - dateTime.second=systemTime.wSecond; - dateTime.milliseconds=systemTime.wMilliseconds; - - FILETIME fileTime; - SystemTimeToFileTime(&systemTime, &fileTime); - ULARGE_INTEGER largeInteger; - largeInteger.HighPart=fileTime.dwHighDateTime; - largeInteger.LowPart=fileTime.dwLowDateTime; - dateTime.filetime=largeInteger.QuadPart; - dateTime.totalMilliseconds=dateTime.filetime/10000; - - return dateTime; - } - - SYSTEMTIME DateTimeToSystemTime(const DateTime& dateTime) - { - ULARGE_INTEGER largeInteger; - largeInteger.QuadPart=dateTime.filetime; - FILETIME fileTime; - fileTime.dwHighDateTime=largeInteger.HighPart; - fileTime.dwLowDateTime=largeInteger.LowPart; - - SYSTEMTIME systemTime; - FileTimeToSystemTime(&fileTime, &systemTime); - return systemTime; - } -#elif defined VCZH_GCC - DateTime ConvertTMToDateTime(tm* timeinfo, vint milliseconds) - { - time_t timer = mktime(timeinfo); - DateTime dt; - dt.year = timeinfo->tm_year+1900; - dt.month = timeinfo->tm_mon+1; - dt.day = timeinfo->tm_mday; - dt.dayOfWeek = timeinfo->tm_wday; - dt.hour = timeinfo->tm_hour; - dt.minute = timeinfo->tm_min; - dt.second = timeinfo->tm_sec; - dt.milliseconds = milliseconds; - - // in Linux and macOS, filetime will be mktime(t) * 1000 + gettimeofday().tv_usec / 1000 - dt.filetime = (vuint64_t)timer * 1000 + milliseconds; - dt.totalMilliseconds = (vuint64_t)timer * 1000 + milliseconds; - return dt; - } -#endif - - DateTime DateTime::LocalTime() - { -#if defined VCZH_MSVC - SYSTEMTIME systemTime; - GetLocalTime(&systemTime); - return SystemTimeToDateTime(systemTime); -#elif defined VCZH_GCC - struct timeval tv; - gettimeofday(&tv, nullptr); - tm* timeinfo = localtime(&tv.tv_sec); - return ConvertTMToDateTime(timeinfo, tv.tv_usec / 1000); -#endif - } - - DateTime DateTime::UtcTime() - { -#if defined VCZH_MSVC - SYSTEMTIME utcTime; - GetSystemTime(&utcTime); - return SystemTimeToDateTime(utcTime); -#elif defined VCZH_GCC - struct timeval tv; - gettimeofday(&tv, nullptr); - tm* timeinfo = gmtime(&tv.tv_sec); - return ConvertTMToDateTime(timeinfo, tv.tv_usec / 1000); -#endif - } - - DateTime DateTime::FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds) - { -#if defined VCZH_MSVC - SYSTEMTIME systemTime; - memset(&systemTime, 0, sizeof(systemTime)); - systemTime.wYear=(WORD)_year; - systemTime.wMonth=(WORD)_month; - systemTime.wDay=(WORD)_day; - systemTime.wHour=(WORD)_hour; - systemTime.wMinute=(WORD)_minute; - systemTime.wSecond=(WORD)_second; - systemTime.wMilliseconds=(WORD)_milliseconds; - - FILETIME fileTime; - SystemTimeToFileTime(&systemTime, &fileTime); - FileTimeToSystemTime(&fileTime, &systemTime); - return SystemTimeToDateTime(systemTime); -#elif defined VCZH_GCC - tm timeinfo; - memset(&timeinfo, 0, sizeof(timeinfo)); - timeinfo.tm_year = _year-1900; - timeinfo.tm_mon = _month-1; - timeinfo.tm_mday = _day; - timeinfo.tm_hour = _hour; - timeinfo.tm_min = _minute; - timeinfo.tm_sec = _second; - timeinfo.tm_isdst = -1; - - return ConvertTMToDateTime(&timeinfo, _milliseconds); -#endif - } - - DateTime DateTime::FromFileTime(vuint64_t filetime) - { -#if defined VCZH_MSVC - ULARGE_INTEGER largeInteger; - largeInteger.QuadPart=filetime; - FILETIME fileTime; - fileTime.dwHighDateTime=largeInteger.HighPart; - fileTime.dwLowDateTime=largeInteger.LowPart; - - SYSTEMTIME systemTime; - FileTimeToSystemTime(&fileTime, &systemTime); - return SystemTimeToDateTime(systemTime); -#elif defined VCZH_GCC - time_t timer = (time_t)(filetime / 1000); - tm* timeinfo = localtime(&timer); - return ConvertTMToDateTime(timeinfo, filetime % 1000); -#endif - } - - DateTime::DateTime() - :year(0) - ,month(0) - ,day(0) - ,hour(0) - ,minute(0) - ,second(0) - ,milliseconds(0) - ,filetime(0) - { - } - - DateTime DateTime::ToLocalTime() - { -#if defined VCZH_MSVC - SYSTEMTIME utcTime=DateTimeToSystemTime(*this); - SYSTEMTIME localTime; - SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime); - return SystemTimeToDateTime(localTime); -#elif defined VCZH_GCC - time_t localTimer = time(nullptr); - time_t utcTimer = mktime(gmtime(&localTimer)); - time_t timer = (time_t)(filetime / 1000) + localTimer - utcTimer; - tm* timeinfo = localtime(&timer); - - return ConvertTMToDateTime(timeinfo, milliseconds); -#endif - } - - DateTime DateTime::ToUtcTime() - { -#if defined VCZH_MSVC - SYSTEMTIME localTime=DateTimeToSystemTime(*this); - SYSTEMTIME utcTime; - TzSpecificLocalTimeToSystemTime(NULL, &localTime, &utcTime); - return SystemTimeToDateTime(utcTime); -#elif defined VCZH_GCC - time_t timer = (time_t)(filetime / 1000); - tm* timeinfo = gmtime(&timer); - - return ConvertTMToDateTime(timeinfo, milliseconds); -#endif - } - - DateTime DateTime::Forward(vuint64_t milliseconds) - { -#if defined VCZH_MSVC - return FromFileTime(filetime+milliseconds*10000); -#elif defined VCZH_GCC - return FromFileTime(filetime+milliseconds); -#endif - } - - DateTime DateTime::Backward(vuint64_t milliseconds) - { -#if defined VCZH_MSVC - return FromFileTime(filetime-milliseconds*10000); -#elif defined VCZH_GCC - return FromFileTime(filetime-milliseconds); -#endif } } @@ -630,8 +320,7 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#elif defined VCZH_GCC +#if defined VCZH_GCC #include #include #include @@ -878,24 +567,6 @@ UtfConversion String Conversions (buffer walkthrough) ***********************************************************************/ - vint _wtoa(const wchar_t* w, char* a, vint chars) - { -#if defined VCZH_MSVC - return WideCharToMultiByte(CP_THREAD_ACP, 0, w, -1, a, (int)(a ? chars : 0), 0, 0); -#elif defined VCZH_GCC - return wcstombs(a, w, chars - 1) + 1; -#endif - } - - vint _atow(const char* a, wchar_t* w, vint chars) - { -#if defined VCZH_MSVC - return MultiByteToWideChar(CP_THREAD_ACP, 0, a, -1, w, (int)(w ? chars : 0)); -#elif defined VCZH_GCC - return mbstowcs(w, a, chars - 1) + 1; -#endif - } - template vint _utftoutf_reader(const TFrom* s, TTo* d, vint chars) { @@ -1012,6 +683,7 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ +#include namespace vl { @@ -1532,8 +1204,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#ifdef VCZH_MSVC -#endif namespace vl { @@ -1627,7 +1297,7 @@ UnitTest { SuppressCppFailure(std::forward(callback)); } - __except (EXCEPTION_EXECUTE_HANDLER) + __except (/*EXCEPTION_EXECUTE_HANDLER*/ 1) { RecordFailure(L"Runtime exception occurred!"); } @@ -1680,50 +1350,39 @@ UnitTest Console::SetColor(true, true, true, false); } -#ifdef VCZH_MSVC - int UnitTest::RunAndDisposeTests(int argc, wchar_t* argv[]) -#else - int UnitTest::RunAndDisposeTests(int argc, char* argv[]) -#endif + int UnitTest::PrintUsages() { - if (argc < 3) + PrintMessage(L"Usage: [/D | /R]", MessageKind::Error); + return 1; + } + + int UnitTest::RunAndDisposeTests(Nullable option) + { + if (option) { - if (argc == 2) + if (option.Value() == L"/D") { -#ifdef VCZH_MSVC - WString option = argv[1]; -#else - WString option = atow(argv[1]); -#endif - if (option == L"/D") - { - suppressFailure = false; - } - else if (option == L"/R") - { - suppressFailure = true; - } - else - { - goto PRINT_USAGE; - } + suppressFailure = false; + } + else if (option.Value() == L"/R") + { + suppressFailure = true; } else { -#ifdef VCZH_MSVC - if (IsDebuggerPresent()) - { - suppressFailure = false; - } - else - { - suppressFailure = true; - } -#else - suppressFailure = true; -#endif + return PrintUsages(); } + } + else if (IsDebuggerAttached()) + { + suppressFailure = false; + } + else + { + suppressFailure = true; + } + { auto current = testHead; testHead = nullptr; testTail = nullptr; @@ -1765,9 +1424,44 @@ UnitTest testContext = nullptr; return passed ? 0 : 1; } - PRINT_USAGE: - PrintMessage(L"Usage: [/D | /R]", MessageKind::Error); - return 1; + } + + int UnitTest::RunAndDisposeTests(int argc, wchar_t* argv[]) + { + if (argc < 3) + { + if (argc == 2) + { + return RunAndDisposeTests({ argv[1] }); + } + else + { + return RunAndDisposeTests({}); + } + } + else + { + return PrintUsages(); + } + } + + int UnitTest::RunAndDisposeTests(int argc, char* argv[]) + { + if (argc < 3) + { + if (argc == 2) + { + return RunAndDisposeTests({ atow(argv[1]) }); + } + else + { + return RunAndDisposeTests({}); + } + } + else + { + return PrintUsages(); + } } void UnitTest::RegisterTestFile(const char* fileName, UnitTestFileProc testProc) diff --git a/Import/Vlpp.h b/Import/Vlpp.h index 91e2aae6..fbb6d9c9 100644 --- a/Import/Vlpp.h +++ b/Import/Vlpp.h @@ -435,7 +435,110 @@ Interface /*********************************************************************** -.\DATETIME.H +.\COLLECTIONS\PAIR.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#ifndef VCZH_COLLECTIONS_PAIR +#define VCZH_COLLECTIONS_PAIR + + +namespace vl +{ + namespace collections + { + /// A type representing a pair of key and value. + /// Type of the key. + /// Type of the value. + template + class Pair + { + public: + /// The key. + K key; + /// The value. + V value; + + Pair() + { + } + + Pair(const K& _key, const V& _value) + { + key=_key; + value=_value; + } + + Pair(const Pair& pair) + { + key=pair.key; + value=pair.value; + } + + vint CompareTo(const Pair& pair)const + { + if(keypair.key) + { + return 1; + } + else if(valuepair.value) + { + return 1; + } + else + { + return 0; + } + } + + bool operator==(const Pair& pair)const + { + return CompareTo(pair)==0; + } + + bool operator!=(const Pair& pair)const + { + return CompareTo(pair)!=0; + } + + bool operator<(const Pair& pair)const + { + return CompareTo(pair)<0; + } + + bool operator<=(const Pair& pair)const + { + return CompareTo(pair)<=0; + } + + bool operator>(const Pair& pair)const + { + return CompareTo(pair)>0; + } + + bool operator>=(const Pair& pair)const + { + return CompareTo(pair)>=0; + } + }; + } +} + +#endif + +/*********************************************************************** +.\PRIMITIVES\DATETIME.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) @@ -456,39 +559,39 @@ Date and Time struct DateTime { /// The year. - vint year; + vint year = 0; /// The month, from 1 to 12. - vint month; + vint month = 0; /// The day, from 1 to 31. - vint day; + vint day = 0; /// The hour, from 0 to 23. - vint hour; + vint hour = 0; /// The minute, from 0 to 59. - vint minute; + vint minute = 0; /// The second, from 0 to 60. - vint second; + vint second = 0; /// The milliseconds, from 0 to 999. - vint milliseconds; + vint milliseconds = 0; /// /// The calculated total milliseconds. It is OS dependent because the start time is different. /// It is from 0 to 6, representing Sunday to Saturday. /// - vint dayOfWeek; + vint dayOfWeek = 0; /// /// The calculated total milliseconds. It is OS dependent because the start time is different. /// You should not rely on the fact about how this value is created. /// The only invariant thing is that, when an date time is earlier than another, the totalMilliseconds is lesser. /// - vuint64_t totalMilliseconds; + vuint64_t totalMilliseconds = 0; /// /// The calculated file time for the date and time. It is OS dependent. /// You should not rely on the fact about how this value is created. /// The only invariant thing is that, when an date time is earlier than another, the filetime is lesser. /// - vuint64_t filetime; + vuint64_t filetime = 0; /// Get the current local time. /// The current local time. @@ -515,7 +618,7 @@ Date and Time static DateTime FromFileTime(vuint64_t filetime); /// Create an empty date time value that is not meaningful. - DateTime(); + DateTime() = default; /// Convert the UTC time to the local time. /// The UTC time. @@ -545,7 +648,7 @@ Date and Time /*********************************************************************** -.\POINTER.H +.\PRIMITIVES\POINTER.H ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) @@ -1147,940 +1250,6 @@ Traits #endif -/*********************************************************************** -.\FUNCTION.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifndef VCZH_FUNCTION -#define VCZH_FUNCTION -#include -namespace vl -{ - template - class Func; - -/*********************************************************************** -vl::Func -***********************************************************************/ - - namespace internal_invokers - { - template - class Invoker : public Object - { - public: - virtual R Invoke(TArgs&& ...args) = 0; - }; - - //------------------------------------------------------ - - template - class StaticInvoker : public Invoker - { - protected: - R(*function)(TArgs ...args); - - public: - StaticInvoker(R(*_function)(TArgs...)) - :function(_function) - { - } - - R Invoke(TArgs&& ...args)override - { - return function(std::forward(args)...); - } - }; - - //------------------------------------------------------ - - template - class MemberInvoker : public Invoker - { - protected: - C* sender; - R(C::*function)(TArgs ...args); - - public: - MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args)) - :sender(_sender) - ,function(_function) - { - } - - R Invoke(TArgs&& ...args)override - { - return (sender->*function)(std::forward(args)...); - } - }; - - //------------------------------------------------------ - - template - class ObjectInvoker : public Invoker - { - protected: - C function; - - public: - ObjectInvoker(const C& _function) - :function(_function) - { - } - - ObjectInvoker(C&& _function) - :function(std::move(_function)) - { - } - - R Invoke(TArgs&& ...args)override - { - return function(std::forward(args)...); - } - }; - - //------------------------------------------------------ - - template - class ObjectInvoker : public Invoker - { - protected: - C function; - - public: - ObjectInvoker(const C& _function) - :function(_function) - { - } - - ObjectInvoker(C&& _function) - :function(std::move(_function)) - { - } - - void Invoke(TArgs&& ...args)override - { - function(std::forward(args)...); - } - }; - } - - /// A type for functors. - /// The return type. - /// Types of parameters. - template - class Func : public Object - { - protected: - Ptr> invoker; - - template - static bool IsEmptyFunc(const Func& function) - { - return !function; - } - - template - static bool IsEmptyFunc(Func& function) - { - return !function; - } - - template - static bool IsEmptyFunc(C&&) - { - return false; - } - public: - typedef R FunctionType(TArgs...); - typedef R ResultType; - - /// Create a null functor. - Func() = default; - - /// Copy a functor. - /// The functor to copy. - Func(const Func& function) = default; - - /// Move a functor. - /// The functor to move. - Func(Func&& function) = default; - - /// Create a functor from a function pointer. - /// The function pointer. - Func(R(*function)(TArgs...)) - { - invoker = new internal_invokers::StaticInvoker(function); - } - - /// Create a functor from a method. - /// Type of the class that this method belongs to. - /// The object that this method belongs to. - /// The method pointer. - template - Func(C* sender, R(C::*function)(TArgs...)) - { - invoker = new internal_invokers::MemberInvoker(sender, function); - } - - /// Create a functor from another compatible functor. - /// Type of the functor to copy. - /// The functor to copy. It could be a lambda expression, or any types that has operator() members. - template || - std::is_convertible_v()(std::declval()...)), R> - >> - Func(C&& function) - { - if (!IsEmptyFunc(function)) - { - invoker = new internal_invokers::ObjectInvoker, R, TArgs...>(std::forward(function)); - } - } - - /// Invoke the function. - /// Returns the function result. It crashes when the functor is null. - /// Arguments to invoke the function. - R operator()(TArgs ...args)const - { - return invoker->Invoke(std::forward(args)...); - } - - Func& operator=(const Func& function) - { - invoker = function.invoker; - return *this; - } - - Func& operator=(const Func&& function) - { - invoker = std::move(function.invoker); - return *this; - } - - bool operator==(const Func& function)const - { - return invoker == function.invoker; - } - - bool operator!=(const Func& function)const - { - return invoker != function.invoker; - } - - /// Test is the functor is non-null. - /// Returns true if the functor is non-null. - operator bool()const - { - return invoker; - } - }; - -/*********************************************************************** -vl::function_lambda::LambdaRetriveType -***********************************************************************/ - - namespace function_lambda - { - template - struct LambdaRetriveType - { - }; - - template - struct LambdaRetriveType - { - typedef Func Type; - typedef R(FunctionType)(TArgs...); - typedef R ResultType; - typedef TypeTuple ParameterTypes; - }; - - template - struct LambdaRetriveType - { - typedef Func Type; - typedef R(FunctionType)(TArgs...); - typedef R ResultType; - typedef TypeTuple ParameterTypes; - }; - - /// Create a functor in [T:vl.Func`1] from another functor, with all type arguments autotimatically inferred. The "LAMBDA" macro is recommended for the same purpose for writing compact code. - /// Type of the functor to copy. - /// A copied functor in [T:vl.Func`1]. - /// The functor to copy. - template - typename LambdaRetriveType::Type Lambda(T functionObject) - { - return functionObject; - } - -#define LAMBDA vl::function_lambda::Lambda - } - -/*********************************************************************** -vl::function_binding::Binding -***********************************************************************/ - - namespace function_binding - { - template - struct Binding - { - }; - - template - struct CR{typedef const T& Type;}; - template - struct CR{typedef T& Type;}; - template - struct CR{typedef const T& Type;}; - template - struct CR{typedef const T& Type;}; - - template - struct Binding - { - typedef R FunctionType(T0, TArgs...); - typedef R CurriedType(TArgs...); - typedef T0 FirstParameterType; - - class Binder : public Object - { - protected: - Func target; - T0 firstArgument; - public: - Binder(const Func& _target, T0 _firstArgument) - :target(_target) - ,firstArgument(std::forward(_firstArgument)) - { - } - - R operator()(TArgs ...args)const - { - return target(firstArgument, args...); - } - }; - - class Currier : public Object - { - protected: - Func target; - public: - Currier(const Func& _target) - :target(_target) - { - } - - Func operator()(T0 firstArgument)const - { - return Binder(target, firstArgument); - } - }; - }; - } - - /// - /// Currize a function pointer. - /// Currizing means to create a new functor whose argument is the first argument of the original function. - /// Calling this functor will return another functor whose arguments are all remaining arguments of the original function. - /// Calling the returned function will call the original function. - /// - /// Type of the function pointer. - /// The currized functor. - /// The function pointer to currize. - template - Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> - Curry(T* function) - { - return typename function_binding::Binding::Currier(function); - } - - /// - /// Currize a functor in [T:vl.Func`1]. - /// Currizing means to create a new functor whose argument is the first argument of the original function. - /// Calling this functor will return another functor whose arguments are all remaining arguments of the original function. - /// Calling the returned function will call the original function. - /// - /// Type of the functor. - /// The currized functor. - /// The functor to currize. - template - Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> - Curry(const Func& function) - { - return typename function_binding::Binding::Currier(function); - } -} -#endif - -/*********************************************************************** -.\LAZY.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifndef VCZH_LAZY -#define VCZH_LAZY - - -namespace vl -{ - /// A type representing a lazy evaluation. - /// The type of the evaluation result. - template - class Lazy : public Object - { - protected: - class Internal - { - public: - Func evaluator; - T value; - bool evaluated; - }; - - Ptr internalValue; - public: - /// Create an empty evaluation. - Lazy() = default; - - /// Create an evaluation using a function, which produces the evaluation result. - /// The function. - Lazy(const Func& evaluator) - { - internalValue=new Internal; - internalValue->evaluated=false; - internalValue->evaluator=evaluator; - } - - /// Create an evaluation with the immediate result. - /// The result.0 - Lazy(const T& value) - { - internalValue=new Internal; - internalValue->evaluated=true; - internalValue->value=value; - } - - /// Create an evaluation by copying another one. - /// The evaluation to copy. - Lazy(const Lazy& lazy) = default; - - /// Create an evaluation by moving another one. - /// The evaluation to move. - Lazy(Lazy&& lazy) = default; - - Lazy& operator=(const Func& evaluator) - { - internalValue=new Internal; - internalValue->evaluated=false; - internalValue->evaluator=evaluator; - return *this; - } - - Lazy& operator=(const T& value) - { - internalValue=new Internal; - internalValue->evaluated=true; - internalValue->value=value; - return *this; - } - - Lazy& operator=(const Lazy& lazy) - { - internalValue=lazy.internalValue; - return *this; - } - - /// Get the evaluation result. If the evaluation has not been performed, it will run the evaluation function and cache the result. - /// The evaluation result. - const T& Value()const - { - if(!internalValue->evaluated) - { - internalValue->evaluated=true; - internalValue->value=internalValue->evaluator(); - internalValue->evaluator=Func(); - } - return internalValue->value; - } - - /// Test if it has already been evaluated or not. - /// Returns true if it has already been evaluated. - bool IsEvaluated()const - { - return internalValue->evaluated; - } - - /// Test if it is an empty evaluation or not. - /// Returns true if it is not empty. - operator bool()const - { - return internalValue; - } - }; -} - -#endif - - -/*********************************************************************** -.\TUPLE.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License - -This file is generated by: Vczh Functional Macro -***********************************************************************/ -#ifndef VCZH_TUPLE -#define VCZH_TUPLE - - -namespace vl -{ - class TupleNullItem - { - }; - template - class Tuple - { - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0; - - Tuple() - { - } - - Tuple(T0 p0) - :f0(p0) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1) - :f0(p0),f1(p1) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2) - :f0(p0),f1(p1),f2(p2) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3) - :f0(p0),f1(p1),f2(p2),f3(p3) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -/*********************************************************************** -vl::Tuple -***********************************************************************/ - template - class Tuple : public Object - { - public: - T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8;T9 f9; - - Tuple() - { - } - - Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8,T9 p9) - :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8),f9(p9) - { - } - - static int Compare(const Tuple& a, const Tuple& b) - { - if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1;if (a.f9 < b.f9) return -1; else if (a.f9 > b.f9) return 1; - return 0; - } - - bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } - bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } - bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } - bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } - bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } - bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } - }; - -} -#endif - -/*********************************************************************** -.\COLLECTIONS\PAIR.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifndef VCZH_COLLECTIONS_PAIR -#define VCZH_COLLECTIONS_PAIR - - -namespace vl -{ - namespace collections - { - /// A type representing a pair of key and value. - /// Type of the key. - /// Type of the value. - template - class Pair - { - public: - /// The key. - K key; - /// The value. - V value; - - Pair() - { - } - - Pair(const K& _key, const V& _value) - { - key=_key; - value=_value; - } - - Pair(const Pair& pair) - { - key=pair.key; - value=pair.value; - } - - vint CompareTo(const Pair& pair)const - { - if(keypair.key) - { - return 1; - } - else if(valuepair.value) - { - return 1; - } - else - { - return 0; - } - } - - bool operator==(const Pair& pair)const - { - return CompareTo(pair)==0; - } - - bool operator!=(const Pair& pair)const - { - return CompareTo(pair)!=0; - } - - bool operator<(const Pair& pair)const - { - return CompareTo(pair)<0; - } - - bool operator<=(const Pair& pair)const - { - return CompareTo(pair)<=0; - } - - bool operator>(const Pair& pair)const - { - return CompareTo(pair)>0; - } - - bool operator>=(const Pair& pair)const - { - return CompareTo(pair)>=0; - } - }; - } -} - -#endif - /*********************************************************************** .\COLLECTIONS\INTERFACES.H ***********************************************************************/ @@ -4660,85 +3829,6 @@ Pairwise #endif -/*********************************************************************** -.\COLLECTIONS\OPERATIONSELECT.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifndef VCZH_COLLECTIONS_OPERATIONSELECT -#define VCZH_COLLECTIONS_OPERATIONSELECT - - -namespace vl -{ - namespace collections - { - -/*********************************************************************** -Select -***********************************************************************/ - - template - class SelectEnumerator : public virtual IEnumerator - { - protected: - IEnumerator* enumerator; - Func selector; - Nullable current; - public: - SelectEnumerator(IEnumerator* _enumerator, const Func& _selector, Nullable _current = {}) - :enumerator(_enumerator) - ,selector(_selector) - ,current(_current) - { - } - - ~SelectEnumerator() - { - delete enumerator; - } - - IEnumerator* Clone()const override - { - return new SelectEnumerator(enumerator->Clone(), selector, current); - } - - const K& Current()const override - { - return current.Value(); - } - - vint Index()const override - { - return enumerator->Index(); - } - - bool Next()override - { - if (enumerator->Next()) - { - current = selector(enumerator->Current()); - return true; - } - else - { - return false; - } - } - - void Reset()override - { - enumerator->Reset(); - } - }; - } -} - -#endif - /*********************************************************************** .\COLLECTIONS\OPERATIONSEQUENCE.H ***********************************************************************/ @@ -5263,194 +4353,6 @@ Intersect/Except #endif -/*********************************************************************** -.\COLLECTIONS\OPERATIONWHERE.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifndef VCZH_COLLECTIONS_OPERATIONWHERE -#define VCZH_COLLECTIONS_OPERATIONWHERE - - -namespace vl -{ - namespace collections - { -/*********************************************************************** -Where -***********************************************************************/ - - template - class WhereEnumerator : public virtual IEnumerator - { - protected: - IEnumerator* enumerator; - Func selector; - vint index; - - public: - WhereEnumerator(IEnumerator* _enumerator, const Func& _selector, vint _index=-1) - :enumerator(_enumerator) - ,selector(_selector) - ,index(_index) - { - } - - ~WhereEnumerator() - { - delete enumerator; - } - - IEnumerator* Clone()const override - { - return new WhereEnumerator(enumerator->Clone(), selector, index); - } - - const T& Current()const override - { - return enumerator->Current(); - } - - vint Index()const override - { - return index; - } - - bool Next()override - { - while(enumerator->Next()) - { - if(selector(enumerator->Current())) - { - index++; - return true; - } - } - return false; - } - - void Reset()override - { - enumerator->Reset(); - index=-1; - } - }; - } -} - -#endif - -/*********************************************************************** -.\EVENT.H -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ -#ifndef VCZH_EVENT -#define VCZH_EVENT - - -namespace vl -{ - template - class Event; - - class EventHandler : public Object - { - public: - virtual bool IsAttached() = 0; - }; - - /// An event for being subscribed using multiple callbacks. A callback is any functor that returns void. - /// Types of callback parameters. - template - class Event : public Object - { - protected: - class EventHandlerImpl : public EventHandler - { - public: - bool attached; - Func function; - - EventHandlerImpl(const Func& _function) - :attached(true) - , function(_function) - { - } - - bool IsAttached()override - { - return attached; - } - }; - - collections::SortedList> handlers; - public: - NOT_COPYABLE(Event); - Event() = default; - - /// Add a callback to the event. - /// The event handler representing the callback. - /// The callback. - Ptr Add(const Func& function) - { - Ptr handler = new EventHandlerImpl(function); - handlers.Add(handler); - return handler; - } - - /// Add a callback to the event. - /// The event handler representing the callback. - /// The callback. - Ptr Add(void(*function)(TArgs...)) - { - return Add(Func(function)); - } - - /// Add a method callback to the event. - /// Type of the class that the callback belongs to. - /// The event handler representing the callback. - /// The object that the callback belongs to. - /// The method callback. - template - Ptr Add(C* sender, void(C::*function)(TArgs...)) - { - return Add(Func(sender, function)); - } - - /// Remove a callback by an event handler returns from . - /// Returns true if this operation succeeded. - /// The event handler representing the callback. - bool Remove(Ptr handler) - { - auto impl = handler.Cast(); - if (!impl) return false; - vint index = handlers.IndexOf(impl.Obj()); - if (index == -1) return false; - impl->attached = false; - handlers.RemoveAt(index); - return true; - } - - /// Invoke all callbacks in the event. - /// Arguments to invoke all callbacks. - void operator()(TArgs ...args)const - { - for(vint i = 0; i < handlers.Count(); i++) - { - handlers[i]->function(args...); - } - } - }; -} -#endif - - /*********************************************************************** .\COLLECTIONS\PARTIALORDERING.H ***********************************************************************/ @@ -5939,6 +4841,1104 @@ Partial Ordering #endif +/*********************************************************************** +.\PRIMITIVES\FUNCTION.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#ifndef VCZH_FUNCTION +#define VCZH_FUNCTION +#include +namespace vl +{ + template + class Func; + +/*********************************************************************** +vl::Func +***********************************************************************/ + + namespace internal_invokers + { + template + class Invoker : public Object + { + public: + virtual R Invoke(TArgs&& ...args) = 0; + }; + + //------------------------------------------------------ + + template + class StaticInvoker : public Invoker + { + protected: + R(*function)(TArgs ...args); + + public: + StaticInvoker(R(*_function)(TArgs...)) + :function(_function) + { + } + + R Invoke(TArgs&& ...args)override + { + return function(std::forward(args)...); + } + }; + + //------------------------------------------------------ + + template + class MemberInvoker : public Invoker + { + protected: + C* sender; + R(C::*function)(TArgs ...args); + + public: + MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args)) + :sender(_sender) + ,function(_function) + { + } + + R Invoke(TArgs&& ...args)override + { + return (sender->*function)(std::forward(args)...); + } + }; + + //------------------------------------------------------ + + template + class ObjectInvoker : public Invoker + { + protected: + C function; + + public: + ObjectInvoker(const C& _function) + :function(_function) + { + } + + ObjectInvoker(C&& _function) + :function(std::move(_function)) + { + } + + R Invoke(TArgs&& ...args)override + { + return function(std::forward(args)...); + } + }; + + //------------------------------------------------------ + + template + class ObjectInvoker : public Invoker + { + protected: + C function; + + public: + ObjectInvoker(const C& _function) + :function(_function) + { + } + + ObjectInvoker(C&& _function) + :function(std::move(_function)) + { + } + + void Invoke(TArgs&& ...args)override + { + function(std::forward(args)...); + } + }; + } + + /// A type for functors. + /// The return type. + /// Types of parameters. + template + class Func : public Object + { + protected: + Ptr> invoker; + + template + static bool IsEmptyFunc(const Func& function) + { + return !function; + } + + template + static bool IsEmptyFunc(Func& function) + { + return !function; + } + + template + static bool IsEmptyFunc(C&&) + { + return false; + } + public: + typedef R FunctionType(TArgs...); + typedef R ResultType; + + /// Create a null functor. + Func() = default; + + /// Copy a functor. + /// The functor to copy. + Func(const Func& function) = default; + + /// Move a functor. + /// The functor to move. + Func(Func&& function) = default; + + /// Create a functor from a function pointer. + /// The function pointer. + Func(R(*function)(TArgs...)) + { + invoker = new internal_invokers::StaticInvoker(function); + } + + /// Create a functor from a method. + /// Type of the class that this method belongs to. + /// The object that this method belongs to. + /// The method pointer. + template + Func(C* sender, R(C::*function)(TArgs...)) + { + invoker = new internal_invokers::MemberInvoker(sender, function); + } + + /// Create a functor from another compatible functor. + /// Type of the functor to copy. + /// The functor to copy. It could be a lambda expression, or any types that has operator() members. + template || + std::is_convertible_v()(std::declval()...)), R> + >> + Func(C&& function) + { + if (!IsEmptyFunc(function)) + { + invoker = new internal_invokers::ObjectInvoker, R, TArgs...>(std::forward(function)); + } + } + + /// Invoke the function. + /// Returns the function result. It crashes when the functor is null. + /// Arguments to invoke the function. + R operator()(TArgs ...args)const + { + return invoker->Invoke(std::forward(args)...); + } + + Func& operator=(const Func& function) + { + invoker = function.invoker; + return *this; + } + + Func& operator=(const Func&& function) + { + invoker = std::move(function.invoker); + return *this; + } + + bool operator==(const Func& function)const + { + return invoker == function.invoker; + } + + bool operator!=(const Func& function)const + { + return invoker != function.invoker; + } + + /// Test is the functor is non-null. + /// Returns true if the functor is non-null. + operator bool()const + { + return invoker; + } + }; + +/*********************************************************************** +vl::function_lambda::LambdaRetriveType +***********************************************************************/ + + namespace function_lambda + { + template + struct LambdaRetriveType + { + }; + + template + struct LambdaRetriveType + { + typedef Func Type; + typedef R(FunctionType)(TArgs...); + typedef R ResultType; + typedef TypeTuple ParameterTypes; + }; + + template + struct LambdaRetriveType + { + typedef Func Type; + typedef R(FunctionType)(TArgs...); + typedef R ResultType; + typedef TypeTuple ParameterTypes; + }; + + /// Create a functor in [T:vl.Func`1] from another functor, with all type arguments autotimatically inferred. The "LAMBDA" macro is recommended for the same purpose for writing compact code. + /// Type of the functor to copy. + /// A copied functor in [T:vl.Func`1]. + /// The functor to copy. + template + typename LambdaRetriveType::Type Lambda(T functionObject) + { + return functionObject; + } + +#define LAMBDA vl::function_lambda::Lambda + } + +/*********************************************************************** +vl::function_binding::Binding +***********************************************************************/ + + namespace function_binding + { + template + struct Binding + { + }; + + template + struct CR{typedef const T& Type;}; + template + struct CR{typedef T& Type;}; + template + struct CR{typedef const T& Type;}; + template + struct CR{typedef const T& Type;}; + + template + struct Binding + { + typedef R FunctionType(T0, TArgs...); + typedef R CurriedType(TArgs...); + typedef T0 FirstParameterType; + + class Binder : public Object + { + protected: + Func target; + T0 firstArgument; + public: + Binder(const Func& _target, T0 _firstArgument) + :target(_target) + ,firstArgument(std::forward(_firstArgument)) + { + } + + R operator()(TArgs ...args)const + { + return target(firstArgument, args...); + } + }; + + class Currier : public Object + { + protected: + Func target; + public: + Currier(const Func& _target) + :target(_target) + { + } + + Func operator()(T0 firstArgument)const + { + return Binder(target, firstArgument); + } + }; + }; + } + + /// + /// Currize a function pointer. + /// Currizing means to create a new functor whose argument is the first argument of the original function. + /// Calling this functor will return another functor whose arguments are all remaining arguments of the original function. + /// Calling the returned function will call the original function. + /// + /// Type of the function pointer. + /// The currized functor. + /// The function pointer to currize. + template + Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> + Curry(T* function) + { + return typename function_binding::Binding::Currier(function); + } + + /// + /// Currize a functor in [T:vl.Func`1]. + /// Currizing means to create a new functor whose argument is the first argument of the original function. + /// Calling this functor will return another functor whose arguments are all remaining arguments of the original function. + /// Calling the returned function will call the original function. + /// + /// Type of the functor. + /// The currized functor. + /// The functor to currize. + template + Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> + Curry(const Func& function) + { + return typename function_binding::Binding::Currier(function); + } +} +#endif + +/*********************************************************************** +.\COLLECTIONS\OPERATIONSELECT.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#ifndef VCZH_COLLECTIONS_OPERATIONSELECT +#define VCZH_COLLECTIONS_OPERATIONSELECT + + +namespace vl +{ + namespace collections + { + +/*********************************************************************** +Select +***********************************************************************/ + + template + class SelectEnumerator : public virtual IEnumerator + { + protected: + IEnumerator* enumerator; + Func selector; + Nullable current; + public: + SelectEnumerator(IEnumerator* _enumerator, const Func& _selector, Nullable _current = {}) + :enumerator(_enumerator) + ,selector(_selector) + ,current(_current) + { + } + + ~SelectEnumerator() + { + delete enumerator; + } + + IEnumerator* Clone()const override + { + return new SelectEnumerator(enumerator->Clone(), selector, current); + } + + const K& Current()const override + { + return current.Value(); + } + + vint Index()const override + { + return enumerator->Index(); + } + + bool Next()override + { + if (enumerator->Next()) + { + current = selector(enumerator->Current()); + return true; + } + else + { + return false; + } + } + + void Reset()override + { + enumerator->Reset(); + } + }; + } +} + +#endif + +/*********************************************************************** +.\COLLECTIONS\OPERATIONWHERE.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#ifndef VCZH_COLLECTIONS_OPERATIONWHERE +#define VCZH_COLLECTIONS_OPERATIONWHERE + + +namespace vl +{ + namespace collections + { +/*********************************************************************** +Where +***********************************************************************/ + + template + class WhereEnumerator : public virtual IEnumerator + { + protected: + IEnumerator* enumerator; + Func selector; + vint index; + + public: + WhereEnumerator(IEnumerator* _enumerator, const Func& _selector, vint _index=-1) + :enumerator(_enumerator) + ,selector(_selector) + ,index(_index) + { + } + + ~WhereEnumerator() + { + delete enumerator; + } + + IEnumerator* Clone()const override + { + return new WhereEnumerator(enumerator->Clone(), selector, index); + } + + const T& Current()const override + { + return enumerator->Current(); + } + + vint Index()const override + { + return index; + } + + bool Next()override + { + while(enumerator->Next()) + { + if(selector(enumerator->Current())) + { + index++; + return true; + } + } + return false; + } + + void Reset()override + { + enumerator->Reset(); + index=-1; + } + }; + } +} + +#endif + +/*********************************************************************** +.\PRIMITIVES\EVENT.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ +#ifndef VCZH_EVENT +#define VCZH_EVENT + + +namespace vl +{ + template + class Event; + + class EventHandler : public Object + { + public: + virtual bool IsAttached() = 0; + }; + + /// An event for being subscribed using multiple callbacks. A callback is any functor that returns void. + /// Types of callback parameters. + template + class Event : public Object + { + protected: + class EventHandlerImpl : public EventHandler + { + public: + bool attached; + Func function; + + EventHandlerImpl(const Func& _function) + :attached(true) + , function(_function) + { + } + + bool IsAttached()override + { + return attached; + } + }; + + collections::SortedList> handlers; + public: + NOT_COPYABLE(Event); + Event() = default; + + /// Add a callback to the event. + /// The event handler representing the callback. + /// The callback. + Ptr Add(const Func& function) + { + Ptr handler = new EventHandlerImpl(function); + handlers.Add(handler); + return handler; + } + + /// Add a callback to the event. + /// The event handler representing the callback. + /// The callback. + Ptr Add(void(*function)(TArgs...)) + { + return Add(Func(function)); + } + + /// Add a method callback to the event. + /// Type of the class that the callback belongs to. + /// The event handler representing the callback. + /// The object that the callback belongs to. + /// The method callback. + template + Ptr Add(C* sender, void(C::*function)(TArgs...)) + { + return Add(Func(sender, function)); + } + + /// Remove a callback by an event handler returns from . + /// Returns true if this operation succeeded. + /// The event handler representing the callback. + bool Remove(Ptr handler) + { + auto impl = handler.Cast(); + if (!impl) return false; + vint index = handlers.IndexOf(impl.Obj()); + if (index == -1) return false; + impl->attached = false; + handlers.RemoveAt(index); + return true; + } + + /// Invoke all callbacks in the event. + /// Arguments to invoke all callbacks. + void operator()(TArgs ...args)const + { + for(vint i = 0; i < handlers.Count(); i++) + { + handlers[i]->function(args...); + } + } + }; +} +#endif + + +/*********************************************************************** +.\PRIMITIVES\LAZY.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#ifndef VCZH_LAZY +#define VCZH_LAZY + + +namespace vl +{ + /// A type representing a lazy evaluation. + /// The type of the evaluation result. + template + class Lazy : public Object + { + protected: + class Internal + { + public: + Func evaluator; + T value; + bool evaluated; + }; + + Ptr internalValue; + public: + /// Create an empty evaluation. + Lazy() = default; + + /// Create an evaluation using a function, which produces the evaluation result. + /// The function. + Lazy(const Func& evaluator) + { + internalValue=new Internal; + internalValue->evaluated=false; + internalValue->evaluator=evaluator; + } + + /// Create an evaluation with the immediate result. + /// The result.0 + Lazy(const T& value) + { + internalValue=new Internal; + internalValue->evaluated=true; + internalValue->value=value; + } + + /// Create an evaluation by copying another one. + /// The evaluation to copy. + Lazy(const Lazy& lazy) = default; + + /// Create an evaluation by moving another one. + /// The evaluation to move. + Lazy(Lazy&& lazy) = default; + + Lazy& operator=(const Func& evaluator) + { + internalValue=new Internal; + internalValue->evaluated=false; + internalValue->evaluator=evaluator; + return *this; + } + + Lazy& operator=(const T& value) + { + internalValue=new Internal; + internalValue->evaluated=true; + internalValue->value=value; + return *this; + } + + Lazy& operator=(const Lazy& lazy) + { + internalValue=lazy.internalValue; + return *this; + } + + /// Get the evaluation result. If the evaluation has not been performed, it will run the evaluation function and cache the result. + /// The evaluation result. + const T& Value()const + { + if(!internalValue->evaluated) + { + internalValue->evaluated=true; + internalValue->value=internalValue->evaluator(); + internalValue->evaluator=Func(); + } + return internalValue->value; + } + + /// Test if it has already been evaluated or not. + /// Returns true if it has already been evaluated. + bool IsEvaluated()const + { + return internalValue->evaluated; + } + + /// Test if it is an empty evaluation or not. + /// Returns true if it is not empty. + operator bool()const + { + return internalValue; + } + }; +} + +#endif + + +/*********************************************************************** +.\PRIMITIVES\TUPLE.H +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License + +This file is generated by: Vczh Functional Macro +***********************************************************************/ +#ifndef VCZH_TUPLE +#define VCZH_TUPLE + + +namespace vl +{ + class TupleNullItem + { + }; + template + class Tuple + { + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0; + + Tuple() + { + } + + Tuple(T0 p0) + :f0(p0) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1) + :f0(p0),f1(p1) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2) + :f0(p0),f1(p1),f2(p2) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3) + :f0(p0),f1(p1),f2(p2),f3(p3) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +/*********************************************************************** +vl::Tuple +***********************************************************************/ + template + class Tuple : public Object + { + public: + T0 f0;T1 f1;T2 f2;T3 f3;T4 f4;T5 f5;T6 f6;T7 f7;T8 f8;T9 f9; + + Tuple() + { + } + + Tuple(T0 p0,T1 p1,T2 p2,T3 p3,T4 p4,T5 p5,T6 p6,T7 p7,T8 p8,T9 p9) + :f0(p0),f1(p1),f2(p2),f3(p3),f4(p4),f5(p5),f6(p6),f7(p7),f8(p8),f9(p9) + { + } + + static int Compare(const Tuple& a, const Tuple& b) + { + if (a.f0 < b.f0) return -1; else if (a.f0 > b.f0) return 1;if (a.f1 < b.f1) return -1; else if (a.f1 > b.f1) return 1;if (a.f2 < b.f2) return -1; else if (a.f2 > b.f2) return 1;if (a.f3 < b.f3) return -1; else if (a.f3 > b.f3) return 1;if (a.f4 < b.f4) return -1; else if (a.f4 > b.f4) return 1;if (a.f5 < b.f5) return -1; else if (a.f5 > b.f5) return 1;if (a.f6 < b.f6) return -1; else if (a.f6 > b.f6) return 1;if (a.f7 < b.f7) return -1; else if (a.f7 > b.f7) return 1;if (a.f8 < b.f8) return -1; else if (a.f8 > b.f8) return 1;if (a.f9 < b.f9) return -1; else if (a.f9 > b.f9) return 1; + return 0; + } + + bool operator==(const Tuple& value)const{ return Compare(*this, value) == 0; } + bool operator!=(const Tuple& value)const{ return Compare(*this, value) != 0; } + bool operator< (const Tuple& value)const{ return Compare(*this, value) < 0; } + bool operator<=(const Tuple& value)const{ return Compare(*this, value) <= 0; } + bool operator> (const Tuple& value)const{ return Compare(*this, value) > 0; } + bool operator>=(const Tuple& value)const{ return Compare(*this, value) >= 0; } + }; + +} +#endif + /*********************************************************************** .\STRINGS\STRING.H ***********************************************************************/ @@ -8348,6 +8348,11 @@ namespace vl /// ]]> class UnitTest { + protected: + static bool IsDebuggerAttached(); + static int PrintUsages(); + static int RunAndDisposeTests(Nullable option); + public: UnitTest() = delete; @@ -8366,11 +8371,13 @@ namespace vl /// The return value for the main function. If any assertion fails, it is non-zero. /// Accept the first argument of the main function. /// Accept the second argument of the main function. -#ifdef VCZH_MSVC static int RunAndDisposeTests(int argc, wchar_t* argv[]); -#else + + /// Run all test cases. + /// The return value for the main function. If any assertion fails, it is non-zero. + /// Accept the first argument of the main function. + /// Accept the second argument of the main function. static int RunAndDisposeTests(int argc, char* argv[]); -#endif static void RegisterTestFile(const char* fileName, UnitTestFileProc testProc); static void RunCategoryOrCase(const WString& description, bool isCategory, Func&& callback); diff --git a/Import/VlppOS.Linux.cpp b/Import/VlppOS.Linux.cpp new file mode 100644 index 00000000..3c201d97 --- /dev/null +++ b/Import/VlppOS.Linux.cpp @@ -0,0 +1,1721 @@ +/*********************************************************************** +THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY +DEVELOPER: Zihan Chen(vczh) +***********************************************************************/ +#include "VlppOS.h" +#include "Vlpp.h" + +/*********************************************************************** +.\FILESYSTEM.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include +#include + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + namespace filesystem + { + using namespace collections; + using namespace stream; + +/*********************************************************************** +FilePath +***********************************************************************/ + + const wchar_t FilePath::Delimiter; + + void FilePath::Initialize() + { + { + Array buffer(fullPath.Length() + 1); + wcscpy(&buffer[0], fullPath.Buffer()); + NormalizeDelimiters(buffer); + fullPath = &buffer[0]; + } + + if (fullPath.Length() == 0) + fullPath = L"/"; + + if (fullPath[0] != Delimiter) + { + char buffer[PATH_MAX] = { 0 }; + getcwd(buffer, PATH_MAX); + fullPath = atow(AString(buffer)) + WString::FromChar(Delimiter) + fullPath; + } + + { + collections::List components; + GetPathComponents(fullPath, components); + for (int i = 0; i < components.Count(); i++) + { + if (components[i] == L".") + { + components.RemoveAt(i); + i--; + } + else if (components[i] == L"..") + { + if (i > 0) + { + components.RemoveAt(i); + components.RemoveAt(i - 1); + i -= 2; + } + else + { + throw ArgumentException(L"Illegal path."); + } + } + } + + fullPath = ComponentsToPath(components); + } + + TrimLastDelimiter(fullPath); + } + + bool FilePath::IsFile()const + { + struct stat info; + AString path = wtoa(fullPath); + int result = stat(path.Buffer(), &info); + if(result != 0) return false; + else return S_ISREG(info.st_mode); + } + + bool FilePath::IsFolder()const + { + struct stat info; + AString path = wtoa(fullPath); + int result = stat(path.Buffer(), &info); + if(result != 0) return false; + else return S_ISDIR(info.st_mode); + } + + bool FilePath::IsRoot()const + { + return fullPath == L"/"; + } + + WString FilePath::GetRelativePathFor(const FilePath& _filePath) + { + if (fullPath.Length() == 0 || _filePath.fullPath.Length() == 0 || fullPath[0] != _filePath.fullPath[0]) + { + return _filePath.fullPath; + } + + collections::List srcComponents, tgtComponents, resultComponents; + GetPathComponents(IsFolder() ? fullPath : GetFolder().GetFullPath(), srcComponents); + GetPathComponents(_filePath.fullPath, tgtComponents); + + int minLength = srcComponents.Count() <= tgtComponents.Count() ? srcComponents.Count() : tgtComponents.Count(); + int lastCommonComponent = 0; + for (int i = 0; i < minLength; i++) + { + if (srcComponents[i] == tgtComponents[i]) + { + lastCommonComponent = i; + } + else + break; + } + + for (int i = lastCommonComponent + 1; i < srcComponents.Count(); i++) + { + resultComponents.Add(L".."); + } + + for (int i = lastCommonComponent + 1; i < tgtComponents.Count(); i++) + { + resultComponents.Add(tgtComponents[i]); + } + + return ComponentsToPath(resultComponents); + } + +/*********************************************************************** +File +***********************************************************************/ + + bool File::Delete()const + { + AString path = wtoa(filePath.GetFullPath()); + return unlink(path.Buffer()) == 0; + } + + bool File::Rename(const WString& newName)const + { + AString oldFileName = wtoa(filePath.GetFullPath()); + AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); + return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; + } + +/*********************************************************************** +Folder +***********************************************************************/ + + bool Folder::GetFolders(collections::List& folders)const + { + if (!Exists()) return false; + + DIR *dir; + AString searchPath = wtoa(filePath.GetFullPath()); + + if ((dir = opendir(searchPath.Buffer())) == NULL) + { + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + WString childName = atow(AString(entry->d_name)); + FilePath childFullPath = filePath / childName; + if (childName != L"." && childName != L".." && childFullPath.IsFolder()) + { + folders.Add(Folder(childFullPath)); + } + } + + if (closedir(dir) != 0) + { + return false; + } + + return true; + } + + bool Folder::GetFiles(collections::List& files)const + { + if (!Exists()) return false; + + DIR* dir; + AString searchPath = wtoa(filePath.GetFullPath()); + + if ((dir = opendir(searchPath.Buffer())) == NULL) + { + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + FilePath childFullPath = filePath / (atow(AString(entry->d_name))); + if (childFullPath.IsFile()) + { + files.Add(File(childFullPath)); + } + } + + if (closedir(dir) != 0) + { + return false; + } + + return true; + } + + bool Folder::CreateNonRecursively()const + { + AString path = wtoa(filePath.GetFullPath()); + return mkdir(path.Buffer(), 0777) == 0; + } + + bool Folder::DeleteNonRecursively()const + { + AString path = wtoa(filePath.GetFullPath()); + return rmdir(path.Buffer()) == 0; + } + + bool Folder::Rename(const WString& newName)const + { + AString oldFileName = wtoa(filePath.GetFullPath()); + AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); + return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; + } + } +} + + +/*********************************************************************** +.\LOCALE.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include +#include +#include + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + using namespace collections; + +/*********************************************************************** +Locale +***********************************************************************/ + + Locale Locale::Invariant() + { + return Locale(L""); + } + + Locale Locale::SystemDefault() + { + return Locale(L"en-US"); + } + + Locale Locale::UserDefault() + { + return Locale(L"en-US"); + } + + void Locale::Enumerate(collections::List& locales) + { + locales.Add(Locale(L"en-US")); + } + + void Locale::GetShortDateFormats(collections::List& formats)const + { + formats.Add(L"MM/dd/yyyy"); + formats.Add(L"yyyy-MM-dd"); + } + + void Locale::GetLongDateFormats(collections::List& formats)const + { + formats.Add(L"dddd, dd MMMM yyyy"); + } + + void Locale::GetYearMonthDateFormats(collections::List& formats)const + { + formats.Add(L"yyyy MMMM"); + } + + void Locale::GetLongTimeFormats(collections::List& formats)const + { + formats.Add(L"HH:mm:ss"); + } + + void Locale::GetShortTimeFormats(collections::List& formats)const + { + formats.Add(L"HH:mm"); + formats.Add(L"hh:mm tt"); + } + + WString Locale::FormatDate(const WString& format, DateTime date)const + { + /* + auto df = L"yyyy,MM,MMM,MMMM,dd,ddd,dddd"; + auto ds = L"2000,01,Jan,January,02,Sun,Sunday"; + auto tf = L"hh,HH,mm,ss,tt"; + auto ts = L"01,13,02,03,PM"; + */ + WString result; + const wchar_t* reading = format.Buffer(); + + while (*reading) + { + if (wcsncmp(reading, L"yyyy", 4) == 0) + { + WString fragment = itow(date.year); + while (fragment.Length() < 4) fragment = L"0" + fragment; + result += fragment; + reading += 4; + } + else if (wcsncmp(reading, L"MMMM", 4) == 0) + { + result += GetLongMonthName(date.month); + reading += 4; + } + else if (wcsncmp(reading, L"MMM", 3) == 0) + { + result += GetShortMonthName(date.month); + reading += 3; + } + else if (wcsncmp(reading, L"MM", 2) == 0) + { + WString fragment = itow(date.month); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"dddd", 4) == 0) + { + result += GetLongDayOfWeekName(date.dayOfWeek); + reading += 4; + } + else if (wcsncmp(reading, L"ddd", 3) == 0) + { + result += GetShortDayOfWeekName(date.dayOfWeek); + reading += 3; + } + else if (wcsncmp(reading, L"dd", 2) == 0) + { + WString fragment = itow(date.day); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"hh", 2) == 0) + { + WString fragment = itow(date.hour > 12 ? date.hour - 12 : date.hour); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"HH", 2) == 0) + { + WString fragment = itow(date.hour); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"mm", 2) == 0) + { + WString fragment = itow(date.minute); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"ss", 2) == 0) + { + WString fragment = itow(date.second); + while (fragment.Length() < 2) fragment = L"0" + fragment; + result += fragment; + reading += 2; + } + else if (wcsncmp(reading, L"tt", 2) == 0) + { + result += date.hour > 12 ? L"PM" : L"AM"; + reading += 2; + } + else + { + result += WString::FromChar(*reading); + reading++; + } + } + return result; + } + + WString Locale::FormatTime(const WString& format, DateTime time)const + { + return FormatDate(format, time); + } + + WString Locale::FormatNumber(const WString& number)const + { + return number; + } + + WString Locale::FormatCurrency(const WString& currency)const + { + return currency; + } + + WString Locale::GetShortDayOfWeekName(vint dayOfWeek)const + { + switch (dayOfWeek) + { + case 0: return L"Sun"; + case 1: return L"Mon"; + case 2: return L"Tue"; + case 3: return L"Wed"; + case 4: return L"Thu"; + case 5: return L"Fri"; + case 6: return L"Sat"; + } + return L""; + } + + WString Locale::GetLongDayOfWeekName(vint dayOfWeek)const + { + switch (dayOfWeek) + { + case 0: return L"Sunday"; + case 1: return L"Monday"; + case 2: return L"Tuesday"; + case 3: return L"Wednesday"; + case 4: return L"Thursday"; + case 5: return L"Friday"; + case 6: return L"Saturday"; + } + return L""; + } + + WString Locale::GetShortMonthName(vint month)const + { + switch (month) + { + case 1: return L"Jan"; + case 2: return L"Feb"; + case 3: return L"Mar"; + case 4: return L"Apr"; + case 5: return L"May"; + case 6: return L"Jun"; + case 7: return L"Jul"; + case 8: return L"Aug"; + case 9: return L"Sep"; + case 10: return L"Oct"; + case 11: return L"Nov"; + case 12: return L"Dec"; + } + return L""; + } + + WString Locale::GetLongMonthName(vint month)const + { + switch (month) + { + case 1: return L"January"; + case 2: return L"February"; + case 3: return L"March"; + case 4: return L"April"; + case 5: return L"May"; + case 6: return L"June"; + case 7: return L"July"; + case 8: return L"August"; + case 9: return L"September"; + case 10: return L"October"; + case 11: return L"November"; + case 12: return L"December"; + } + return L""; + } + + WString Locale::ToLower(const WString& str)const + { + return wlower(str); + } + + WString Locale::ToUpper(const WString& str)const + { + return wupper(str); + } + + WString Locale::ToLinguisticLower(const WString& str)const + { + return wlower(str); + } + + WString Locale::ToLinguisticUpper(const WString& str)const + { + return wupper(str); + } + + vint Locale::Compare(const WString& s1, const WString& s2, Normalization normalization)const + { + switch (normalization) + { + case Normalization::None: + return wcscmp(s1.Buffer(), s2.Buffer()); + case Normalization::IgnoreCase: + return wcscasecmp(s1.Buffer(), s2.Buffer()); + default: + return 0; + } + } + + vint Locale::CompareOrdinal(const WString& s1, const WString& s2)const + { + return wcscmp(s1.Buffer(), s2.Buffer()); + } + + vint Locale::CompareOrdinalIgnoreCase(const WString& s1, const WString& s2)const + { + return wcscasecmp(s1.Buffer(), s2.Buffer()); + } + + collections::Pair Locale::FindFirst(const WString& text, const WString& find, Normalization normalization)const + { + if (text.Length() < find.Length() || find.Length() == 0) + { + return Pair(-1, 0); + } + const wchar_t* result = 0; + switch (normalization) + { + case Normalization::None: + { + const wchar_t* reading = text.Buffer(); + while (*reading) + { + if (wcsncmp(reading, find.Buffer(), find.Length()) == 0) + { + result = reading; + break; + } + reading++; + } + } + break; + case Normalization::IgnoreCase: + { + const wchar_t* reading = text.Buffer(); + while (*reading) + { + if (wcsncasecmp(reading, find.Buffer(), find.Length()) == 0) + { + result = reading; + break; + } + reading++; + } + } + break; + } + return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); + } + + collections::Pair Locale::FindLast(const WString& text, const WString& find, Normalization normalization)const + { + if (text.Length() < find.Length() || find.Length() == 0) + { + return Pair(-1, 0); + } + const wchar_t* result = 0; + switch (normalization) + { + case Normalization::None: + { + const wchar_t* reading = text.Buffer(); + while (*reading) + { + if (wcsncmp(reading, find.Buffer(), find.Length()) == 0) + { + result = reading; + } + reading++; + } + } + break; + case Normalization::IgnoreCase: + { + const wchar_t* reading = text.Buffer(); + while (*reading) + { + if (wcsncasecmp(reading, find.Buffer(), find.Length()) == 0) + { + result = reading; + } + reading++; + } + } + break; + } + return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); + } + + bool Locale::StartsWith(const WString& text, const WString& find, Normalization normalization)const + { + if (text.Length() < find.Length() || find.Length() == 0) + { + return false; + } + switch (normalization) + { + case Normalization::None: + return wcsncmp(text.Buffer(), find.Buffer(), find.Length()) == 0; + case Normalization::IgnoreCase: + return wcsncasecmp(text.Buffer(), find.Buffer(), find.Length()) == 0; + } + return false; + } + + bool Locale::EndsWith(const WString& text, const WString& find, Normalization normalization)const + { + if (text.Length() < find.Length() || find.Length() == 0) + { + return false; + } + switch (normalization) + { + case Normalization::None: + return wcsncmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; + case Normalization::IgnoreCase: + return wcsncasecmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; + } + return false; + } +} + + +/*********************************************************************** +.\THREADING.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include +#include +#include +#if defined(__APPLE__) || defined(__APPLE_CC__) +#include +#endif + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + using namespace threading_internal; + using namespace collections; + + +/*********************************************************************** +Thread +***********************************************************************/ + + namespace threading_internal + { + struct ThreadData + { + pthread_t id; + EventObject ev; + }; + + class ProceduredThread : public Thread + { + private: + Thread::ThreadProcedure procedure; + void* argument; + bool deleteAfterStopped; + + protected: + void Run() + { + bool deleteAfterStopped = this->deleteAfterStopped; + ThreadLocalStorage::FixStorages(); + try + { + procedure(this, argument); + threadState=Thread::Stopped; + internalData->ev.Signal(); + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + throw; + } + if(deleteAfterStopped) + { + delete this; + } + } + public: + ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped) + :procedure(_procedure) + ,argument(_argument) + ,deleteAfterStopped(_deleteAfterStopped) + { + } + }; + + class LambdaThread : public Thread + { + private: + Func procedure; + bool deleteAfterStopped; + + protected: + void Run() + { + bool deleteAfterStopped = this->deleteAfterStopped; + ThreadLocalStorage::FixStorages(); + try + { + procedure(); + threadState=Thread::Stopped; + internalData->ev.Signal(); + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + throw; + } + if(deleteAfterStopped) + { + delete this; + } + } + public: + LambdaThread(const Func& _procedure, bool _deleteAfterStopped) + :procedure(_procedure) + ,deleteAfterStopped(_deleteAfterStopped) + { + } + }; + } + + void InternalThreadProc(Thread* thread) + { + thread->Run(); + } + + void* InternalThreadProcWrapper(void* lpParameter) + { + InternalThreadProc((Thread*)lpParameter); + return 0; + } + + Thread::Thread() + { + internalData=new ThreadData; + internalData->ev.CreateManualUnsignal(false); + threadState=Thread::NotStarted; + } + + Thread::~Thread() + { + if (internalData) + { + Stop(); + if (threadState!=Thread::NotStarted) + { + pthread_detach(internalData->id); + } + delete internalData; + } + } + + Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped) + { + if(procedure) + { + Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped); + if(thread->Start()) + { + return thread; + } + else + { + delete thread; + } + } + return 0; + } + + Thread* Thread::CreateAndStart(const Func& procedure, bool deleteAfterStopped) + { + Thread* thread=new LambdaThread(procedure, deleteAfterStopped); + if(thread->Start()) + { + return thread; + } + else + { + delete thread; + } + return 0; + } + + void Thread::Sleep(vint ms) + { + if (ms >= 1000) + { + sleep(ms / 1000); + } + if (ms % 1000) + { + usleep((ms % 1000) * 1000); + } + } + + vint Thread::GetCPUCount() + { + return (vint)sysconf(_SC_NPROCESSORS_ONLN); + } + + vint Thread::GetCurrentThreadId() + { + return (vint)::pthread_self(); + } + + bool Thread::Start() + { + if(threadState==Thread::NotStarted) + { + if(pthread_create(&internalData->id, nullptr, &InternalThreadProcWrapper, this)==0) + { + threadState=Thread::Running; + return true; + } + } + return false; + } + + bool Thread::Wait() + { + return internalData->ev.Wait(); + } + + bool Thread::Stop() + { + if (threadState==Thread::Running) + { + if(pthread_cancel(internalData->id)==0) + { + threadState=Thread::Stopped; + internalData->ev.Signal(); + return true; + } + } + return false; + } + + Thread::ThreadState Thread::GetState() + { + return threadState; + } + +/*********************************************************************** +Mutex +***********************************************************************/ + + namespace threading_internal + { + struct MutexData + { + Semaphore sem; + }; + }; + + Mutex::Mutex() + { + internalData = new MutexData; + } + + Mutex::~Mutex() + { + delete internalData; + } + + bool Mutex::Create(bool owned, const WString& name) + { + return internalData->sem.Create(owned ? 0 : 1, 1, name); + } + + bool Mutex::Open(bool inheritable, const WString& name) + { + return internalData->sem.Open(inheritable, name); + } + + bool Mutex::Release() + { + return internalData->sem.Release(); + } + + bool Mutex::Wait() + { + return internalData->sem.Wait(); + } + +/*********************************************************************** +Semaphore +***********************************************************************/ + + namespace threading_internal + { + struct SemaphoreData + { + sem_t semUnnamed; + sem_t* semNamed = nullptr; + }; + } + + Semaphore::Semaphore() + :internalData(0) + { + } + + Semaphore::~Semaphore() + { + if (internalData) + { + if (internalData->semNamed) + { + sem_close(internalData->semNamed); + } + else + { + sem_destroy(&internalData->semUnnamed); + } + delete internalData; + } + } + + bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name) + { + if (internalData) return false; + if (initialCount > maxCount) return false; + + internalData = new SemaphoreData; +#if defined(__APPLE__) + + AString auuid; + if(name.Length() == 0) + { + CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef cfstr = CFUUIDCreateString(kCFAllocatorDefault, cfuuid); + auuid = CFStringGetCStringPtr(cfstr, kCFStringEncodingASCII); + + CFRelease(cfstr); + CFRelease(cfuuid); + } + auuid = auuid.Insert(0, "/"); + // OSX SEM_NAME_LENGTH = 31 + if(auuid.Length() >= 30) + auuid = auuid.Sub(0, 30); + + if ((internalData->semNamed = sem_open(auuid.Buffer(), O_CREAT, O_RDWR, initialCount)) == SEM_FAILED) + { + delete internalData; + internalData = 0; + return false; + } + +#else + if (name == L"") + { + if(sem_init(&internalData->semUnnamed, 0, (int)initialCount) == -1) + { + delete internalData; + internalData = 0; + return false; + } + } + else + { + AString astr = wtoa(name); + + if ((internalData->semNamed = sem_open(astr.Buffer(), O_CREAT, 0777, initialCount)) == SEM_FAILED) + { + delete internalData; + internalData = 0; + return false; + } + } +#endif + + Release(initialCount); + return true; + } + + bool Semaphore::Open(bool inheritable, const WString& name) + { + if (internalData) return false; + if (inheritable) return false; + + internalData = new SemaphoreData; + if (!(internalData->semNamed = sem_open(wtoa(name).Buffer(), 0))) + { + delete internalData; + internalData = 0; + return false; + } + + return true; + } + + bool Semaphore::Release() + { + return Release(1); + } + + vint Semaphore::Release(vint count) + { + for (vint i = 0; i < count; i++) + { + if (internalData->semNamed) + { + sem_post(internalData->semNamed); + } + else + { + sem_post(&internalData->semUnnamed); + } + } + return true; + } + + bool Semaphore::Wait() + { + if (internalData->semNamed) + { + return sem_wait(internalData->semNamed) == 0; + } + else + { + return sem_wait(&internalData->semUnnamed) == 0; + } + } + +/*********************************************************************** +EventObject +***********************************************************************/ + + namespace threading_internal + { + struct EventData + { + bool autoReset; + volatile bool signaled; + CriticalSection mutex; + ConditionVariable cond; + volatile vint counter = 0; + }; + } + + EventObject::EventObject() + { + internalData = nullptr; + } + + EventObject::~EventObject() + { + if (internalData) + { + delete internalData; + } + } + + bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name) + { + if (name!=L"") return false; + if (internalData) return false; + + internalData = new EventData; + internalData->autoReset = true; + internalData->signaled = signaled; + return true; + } + + bool EventObject::CreateManualUnsignal(bool signaled, const WString& name) + { + if (name!=L"") return false; + if (internalData) return false; + + internalData = new EventData; + internalData->autoReset = false; + internalData->signaled = signaled; + return true; + } + + bool EventObject::Signal() + { + if (!internalData) return false; + + internalData->mutex.Enter(); + internalData->signaled = true; + if (internalData->counter) + { + if (internalData->autoReset) + { + internalData->cond.WakeOnePending(); + internalData->signaled = false; + } + else + { + internalData->cond.WakeAllPendings(); + } + } + internalData->mutex.Leave(); + return true; + } + + bool EventObject::Unsignal() + { + if (!internalData) return false; + + internalData->mutex.Enter(); + internalData->signaled = false; + internalData->mutex.Leave(); + return true; + } + + bool EventObject::Wait() + { + if (!internalData) return false; + + internalData->mutex.Enter(); + if (internalData->signaled) + { + if (internalData->autoReset) + { + internalData->signaled = false; + } + } + else + { + INCRC(&internalData->counter); + internalData->cond.SleepWith(internalData->mutex); + DECRC(&internalData->counter); + } + internalData->mutex.Leave(); + return true; + } + +/*********************************************************************** +ThreadPoolLite +***********************************************************************/ + + namespace threading_internal + { + struct ThreadPoolTask + { + Func task; + Ptr next; + }; + + struct ThreadPoolData + { + Semaphore semaphore; + EventObject taskFinishEvent; + Ptr taskBegin; + Ptr* taskEnd = nullptr; + volatile bool stopping = false; + List taskThreads; + }; + + SpinLock threadPoolLock; + ThreadPoolData* threadPoolData = nullptr; + + void ThreadPoolProc(Thread* thread, void* argument) + { + while (true) + { + Ptr task; + + threadPoolData->semaphore.Wait(); + SPIN_LOCK(threadPoolLock) + { + if (threadPoolData->taskBegin) + { + task = threadPoolData->taskBegin; + threadPoolData->taskBegin = task->next; + } + + if (!threadPoolData->taskBegin) + { + threadPoolData->taskEnd = &threadPoolData->taskBegin; + threadPoolData->taskFinishEvent.Signal(); + } + } + + if (task) + { + ThreadLocalStorage::FixStorages(); + try + { + task->task(); + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + } + } + else if (threadPoolData->stopping) + { + return; + } + } + } + + bool ThreadPoolQueue(const Func& proc) + { + SPIN_LOCK(threadPoolLock) + { + if (!threadPoolData) + { + threadPoolData = new ThreadPoolData; + threadPoolData->semaphore.Create(0, 65536); + threadPoolData->taskFinishEvent.CreateManualUnsignal(false); + threadPoolData->taskEnd = &threadPoolData->taskBegin; + + for (vint i = 0; i < Thread::GetCPUCount() * 4; i++) + { + threadPoolData->taskThreads.Add(Thread::CreateAndStart(&ThreadPoolProc, nullptr, false)); + } + } + + if (threadPoolData) + { + if (threadPoolData->stopping) + { + return false; + } + + auto task = MakePtr(); + task->task = proc; + *threadPoolData->taskEnd = task; + threadPoolData->taskEnd = &task->next; + threadPoolData->semaphore.Release(); + threadPoolData->taskFinishEvent.Unsignal(); + } + } + return true; + } + + bool ThreadPoolStop(bool discardPendingTasks) + { + SPIN_LOCK(threadPoolLock) + { + if (!threadPoolData) return false; + if (threadPoolData->stopping) return false; + + threadPoolData->stopping = true; + if (discardPendingTasks) + { + threadPoolData->taskEnd = &threadPoolData->taskBegin; + threadPoolData->taskBegin = nullptr; + } + + threadPoolData->semaphore.Release(threadPoolData->taskThreads.Count()); + } + + threadPoolData->taskFinishEvent.Wait(); + for (vint i = 0; i < threadPoolData->taskThreads.Count(); i++) + { + auto thread = threadPoolData->taskThreads[i]; + thread->Wait(); + delete thread; + } + threadPoolData->taskThreads.Clear(); + + SPIN_LOCK(threadPoolLock) + { + delete threadPoolData; + threadPoolData = nullptr; + } + return true; + } + } + + ThreadPoolLite::ThreadPoolLite() + { + } + + ThreadPoolLite::~ThreadPoolLite() + { + } + + bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument) + { + return ThreadPoolQueue([proc, argument](){proc(argument);}); + } + + bool ThreadPoolLite::Queue(const Func& proc) + { + return ThreadPoolQueue(proc); + } + + bool ThreadPoolLite::Stop(bool discardPendingTasks) + { + return ThreadPoolStop(discardPendingTasks); + } + +/*********************************************************************** +CriticalSection +***********************************************************************/ + + namespace threading_internal + { + struct CriticalSectionData + { + pthread_mutex_t mutex; + }; + } + + CriticalSection::CriticalSection() + { + internalData = new CriticalSectionData; + pthread_mutex_init(&internalData->mutex, nullptr); + } + + CriticalSection::~CriticalSection() + { + pthread_mutex_destroy(&internalData->mutex); + delete internalData; + } + + bool CriticalSection::TryEnter() + { + return pthread_mutex_trylock(&internalData->mutex) == 0; + } + + void CriticalSection::Enter() + { + pthread_mutex_lock(&internalData->mutex); + } + + void CriticalSection::Leave() + { + pthread_mutex_unlock(&internalData->mutex); + } + + CriticalSection::Scope::Scope(CriticalSection& _criticalSection) + :criticalSection(&_criticalSection) + { + criticalSection->Enter(); + } + + CriticalSection::Scope::~Scope() + { + criticalSection->Leave(); + } + +/*********************************************************************** +ReaderWriterLock +***********************************************************************/ + + namespace threading_internal + { + struct ReaderWriterLockData + { + pthread_rwlock_t rwlock; + }; + } + + ReaderWriterLock::ReaderWriterLock() + { + internalData = new ReaderWriterLockData; + pthread_rwlock_init(&internalData->rwlock, nullptr); + } + + ReaderWriterLock::~ReaderWriterLock() + { + pthread_rwlock_destroy(&internalData->rwlock); + delete internalData; + } + + bool ReaderWriterLock::TryEnterReader() + { + return pthread_rwlock_tryrdlock(&internalData->rwlock) == 0; + } + + void ReaderWriterLock::EnterReader() + { + pthread_rwlock_rdlock(&internalData->rwlock); + } + + void ReaderWriterLock::LeaveReader() + { + pthread_rwlock_unlock(&internalData->rwlock); + } + + bool ReaderWriterLock::TryEnterWriter() + { + return pthread_rwlock_trywrlock(&internalData->rwlock) == 0; + } + + void ReaderWriterLock::EnterWriter() + { + pthread_rwlock_wrlock(&internalData->rwlock); + } + + void ReaderWriterLock::LeaveWriter() + { + pthread_rwlock_unlock(&internalData->rwlock); + } + + ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock) + :lock(&_lock) + { + lock->EnterReader(); + } + + ReaderWriterLock::ReaderScope::~ReaderScope() + { + lock->LeaveReader(); + } + + ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock) + :lock(&_lock) + { + lock->EnterWriter(); + } + + ReaderWriterLock::WriterScope::~WriterScope() + { + lock->LeaveReader(); + } + +/*********************************************************************** +ConditionVariable +***********************************************************************/ + + namespace threading_internal + { + struct ConditionVariableData + { + pthread_cond_t cond; + }; + } + + ConditionVariable::ConditionVariable() + { + internalData = new ConditionVariableData; + pthread_cond_init(&internalData->cond, nullptr); + } + + ConditionVariable::~ConditionVariable() + { + pthread_cond_destroy(&internalData->cond); + delete internalData; + } + + bool ConditionVariable::SleepWith(CriticalSection& cs) + { + return pthread_cond_wait(&internalData->cond, &cs.internalData->mutex) == 0; + } + + void ConditionVariable::WakeOnePending() + { + pthread_cond_signal(&internalData->cond); + } + + void ConditionVariable::WakeAllPendings() + { + pthread_cond_broadcast(&internalData->cond); + } + +/*********************************************************************** +SpinLock +***********************************************************************/ + + SpinLock::Scope::Scope(SpinLock& _spinLock) + :spinLock(&_spinLock) + { + spinLock->Enter(); + } + + SpinLock::Scope::~Scope() + { + spinLock->Leave(); + } + + SpinLock::SpinLock() + :token(0) + { + } + + SpinLock::~SpinLock() + { + } + + bool SpinLock::TryEnter() + { + return __sync_lock_test_and_set(&token, 1)==0; + } + + void SpinLock::Enter() + { + while(__sync_val_compare_and_swap(&token, 0, 1)!=0) + { + while(token!=0) _mm_pause(); + } + } + + void SpinLock::Leave() + { + __sync_lock_test_and_set(&token, 0); + } + +/*********************************************************************** +ThreadLocalStorage +***********************************************************************/ + +#define KEY ((pthread_key_t&)key) + + ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor) + :destructor(_destructor) + { + static_assert(sizeof(key) >= sizeof(pthread_key_t), "ThreadLocalStorage's key storage is not large enouth."); + PushStorage(this); + auto error = pthread_key_create(&KEY, destructor); + CHECK_ERROR(error != EAGAIN && error != ENOMEM, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to create a thread local storage index."); + } + + ThreadLocalStorage::~ThreadLocalStorage() + { + pthread_key_delete(KEY); + } + + void* ThreadLocalStorage::Get() + { + CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage."); + return pthread_getspecific(KEY); + } + + void ThreadLocalStorage::Set(void* data) + { + CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage."); + pthread_setspecific(KEY, data); + } + +#undef KEY +} + + +/*********************************************************************** +.\STREAM\CHARFORMAT.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + namespace stream + { + bool IsMbcsLeadByte(char c) + { + return (vint8_t)c < 0; + } + +/*********************************************************************** +Mbcs +***********************************************************************/ + + vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) + { + WString w = WString::CopyFrom(_buffer, chars); + AString a = wtoa(w); + vint length = a.Length(); + vint result = stream->Write((void*)a.Buffer(), length); + + if (result != length) + { + Close(); + return 0; + } + return chars; + } + + void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars) + { + AString a = AString::CopyFrom(mbcsBuffer, mbcsChars); + WString w = atow(a); + memcpy(wideBuffer, w.Buffer(), wideReaded * sizeof(wchar_t)); + } + +/*********************************************************************** +Utf8 +***********************************************************************/ + + vint Utf8Encoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) + { + WCharToUtfReader reader(_buffer, chars); + while (char8_t c = reader.Read()) + { + vint written = stream->Write(&c, sizeof(c)); + if (written != sizeof(c)) + { + Close(); + return 0; + } + } + if (reader.HasIllegalChar()) + { + Close(); + return 0; + } + return chars; + } + } +} + + +/*********************************************************************** +.\STREAM\CHARFORMAT_TESTENCODING.LINUX.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_GCC +static_assert(false, "Do not build this file for Windows applications."); +#endif + +namespace vl +{ + namespace stream + { +/*********************************************************************** +Helper Functions +***********************************************************************/ + + extern bool CanBeMbcs(unsigned char* buffer, vint size); + extern bool CanBeUtf8(unsigned char* buffer, vint size); + extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs); + extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs); + +/*********************************************************************** +TestEncoding +***********************************************************************/ + + extern void TestEncodingInternal( + unsigned char* buffer, + vint size, + BomEncoder::Encoding& encoding, + bool containsBom, + bool utf16HitSurrogatePairs, + bool utf16BEHitSurrogatePairs, + bool roughMbcs, + bool roughUtf8, + bool roughUtf16, + bool roughUtf16BE + ) + { + if (roughUtf16 && roughUtf16BE && !roughUtf8) + { + if (utf16BEHitSurrogatePairs && !utf16HitSurrogatePairs) + { + encoding = BomEncoder::Utf16BE; + } + else + { + encoding = BomEncoder::Utf16; + } + } + else + { + encoding = BomEncoder::Utf8; + } + } + } +} + diff --git a/Import/VlppOS.Windows.cpp b/Import/VlppOS.Windows.cpp new file mode 100644 index 00000000..7e6afe07 --- /dev/null +++ b/Import/VlppOS.Windows.cpp @@ -0,0 +1,2054 @@ +/*********************************************************************** +THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY +DEVELOPER: Zihan Chen(vczh) +***********************************************************************/ +#include "VlppOS.h" +#include "Vlpp.h" + +/*********************************************************************** +.\FILESYSTEM.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include +#include + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +#pragma comment(lib, "Shlwapi.lib") + +namespace vl +{ + namespace filesystem + { + using namespace collections; + using namespace stream; + +/*********************************************************************** +FilePath +***********************************************************************/ + + void FilePath::Initialize() + { + { + Array buffer(fullPath.Length() + 1); + wcscpy_s(&buffer[0], fullPath.Length() + 1, fullPath.Buffer()); + NormalizeDelimiters(buffer); + fullPath = &buffer[0]; + } + + if (fullPath != L"") + { + if (fullPath.Length() < 2 || fullPath[1] != L':') + { + wchar_t buffer[MAX_PATH + 1] = { 0 }; + auto result = GetCurrentDirectory(sizeof(buffer) / sizeof(*buffer), buffer); + if (result > MAX_PATH + 1 || result == 0) + { + throw ArgumentException(L"Failed to call GetCurrentDirectory.", L"vl::filesystem::FilePath::Initialize", L""); + } + fullPath = WString(buffer) + L"\\" + fullPath; + } + { + wchar_t buffer[MAX_PATH + 1] = { 0 }; + if (fullPath.Length() == 2 && fullPath[1] == L':') + { + fullPath += L"\\"; + } + auto result = GetFullPathName(fullPath.Buffer(), sizeof(buffer) / sizeof(*buffer), buffer, NULL); + if (result > MAX_PATH + 1 || result == 0) + { + throw ArgumentException(L"The path is illegal.", L"vl::filesystem::FilePath::FilePath", L"_filePath"); + } + + { + wchar_t shortPath[MAX_PATH + 1]; + wchar_t longPath[MAX_PATH + 1]; + if (GetShortPathName(buffer, shortPath, MAX_PATH) > 0) + { + if (GetLongPathName(shortPath, longPath, MAX_PATH) > 0) + { + memcpy(buffer, longPath, sizeof(buffer)); + } + } + } + fullPath = buffer; + } + } + + TrimLastDelimiter(fullPath); + } + + bool FilePath::IsFile()const + { + WIN32_FILE_ATTRIBUTE_DATA info; + BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); + if (!result) return false; + return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; + } + + bool FilePath::IsFolder()const + { + WIN32_FILE_ATTRIBUTE_DATA info; + BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); + if (!result) return false; + return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + + bool FilePath::IsRoot()const + { + return fullPath == L""; + } + + WString FilePath::GetRelativePathFor(const FilePath& _filePath) + { + if (fullPath.Length() == 0 || _filePath.fullPath.Length() == 0 || fullPath[0] != _filePath.fullPath[0]) + { + return _filePath.fullPath; + } + + wchar_t buffer[MAX_PATH + 1] = { 0 }; + PathRelativePathTo( + buffer, + fullPath.Buffer(), + (IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0), + _filePath.fullPath.Buffer(), + (_filePath.IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0) + ); + return buffer; + } + +/*********************************************************************** +File +***********************************************************************/ + + bool File::Delete()const + { + return DeleteFile(filePath.GetFullPath().Buffer()) != 0; + } + + bool File::Rename(const WString& newName)const + { + WString oldFileName = filePath.GetFullPath(); + WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); + return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; + } + +/*********************************************************************** +Folder +***********************************************************************/ + + bool Folder::GetFolders(collections::List& folders)const + { + if (filePath.IsRoot()) + { + auto bufferSize = GetLogicalDriveStrings(0, nullptr); + if (bufferSize > 0) + { + Array buffer(bufferSize); + if (GetLogicalDriveStrings((DWORD)buffer.Count(), &buffer[0]) > 0) + { + auto begin = &buffer[0]; + auto end = begin + buffer.Count(); + while (begin < end && *begin) + { + WString driveString = begin; + begin += driveString.Length() + 1; + folders.Add(Folder(FilePath(driveString))); + } + return true; + } + } + return false; + } + else + { + if (!Exists()) return false; + WIN32_FIND_DATA findData; + HANDLE findHandle = INVALID_HANDLE_VALUE; + + while (true) + { + if (findHandle == INVALID_HANDLE_VALUE) + { + WString searchPath = (filePath / L"*").GetFullPath(); + findHandle = FindFirstFile(searchPath.Buffer(), &findData); + if (findHandle == INVALID_HANDLE_VALUE) + { + break; + } + } + else + { + BOOL result = FindNextFile(findHandle, &findData); + if (result == 0) + { + FindClose(findHandle); + break; + } + } + + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0) + { + folders.Add(Folder(filePath / findData.cFileName)); + } + } + } + return true; + } + } + + bool Folder::GetFiles(collections::List& files)const + { + if (filePath.IsRoot()) + { + return true; + } + if (!Exists()) return false; + WIN32_FIND_DATA findData; + HANDLE findHandle = INVALID_HANDLE_VALUE; + + while (true) + { + if (findHandle == INVALID_HANDLE_VALUE) + { + WString searchPath = (filePath / L"*").GetFullPath(); + findHandle = FindFirstFile(searchPath.Buffer(), &findData); + if (findHandle == INVALID_HANDLE_VALUE) + { + break; + } + } + else + { + BOOL result = FindNextFile(findHandle, &findData); + if (result == 0) + { + FindClose(findHandle); + break; + } + } + + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + files.Add(File(filePath / findData.cFileName)); + } + } + return true; + } + + bool Folder::CreateNonRecursively()const + { + return CreateDirectory(filePath.GetFullPath().Buffer(), NULL) != 0; + } + + bool Folder::DeleteNonRecursively()const + { + return RemoveDirectory(filePath.GetFullPath().Buffer()) != 0; + } + + bool Folder::Rename(const WString& newName)const + { + WString oldFileName = filePath.GetFullPath(); + WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); + return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; + } + } +} + + +/*********************************************************************** +.\HTTPUTILITY.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +#pragma comment(lib, "WinHttp.lib") + +namespace vl +{ + using namespace collections; + +/*********************************************************************** +HttpRequest +***********************************************************************/ + + bool HttpRequest::SetHost(const WString& inputQuery) + { + if (method == L"") + { + method = L"GET"; + } + + server = L""; + query = L""; + port = 0; + secure = false; + + { + if (server == L"") + { + if (inputQuery.Length() > 7) + { + WString protocol = inputQuery.Sub(0, 8); + if (_wcsicmp(protocol.Buffer(), L"https://") == 0) + { + const wchar_t* reading = inputQuery.Buffer() + 8; + const wchar_t* index1 = wcschr(reading, L':'); + const wchar_t* index2 = wcschr(reading, L'/'); + if (index2) + { + query = index2; + server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading); + port = INTERNET_DEFAULT_HTTPS_PORT; + secure = true; + if (index1) + { + auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1); + port = _wtoi(portString.Buffer()); + } + return true; + } + } + } + } + if (server == L"") + { + if (inputQuery.Length() > 6) + { + WString protocol = inputQuery.Sub(0, 7); + if (_wcsicmp(protocol.Buffer(), L"http://") == 0) + { + const wchar_t* reading = inputQuery.Buffer() + 7; + const wchar_t* index1 = wcschr(reading, L':'); + const wchar_t* index2 = wcschr(reading, L'/'); + if (index2) + { + query = index2; + server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading); + port = INTERNET_DEFAULT_HTTP_PORT; + if (index1) + { + auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1); + port = _wtoi(portString.Buffer()); + } + return true; + } + } + } + } + } + return false; + } + + void HttpRequest::SetBodyUtf8(const WString& bodyString) + { + vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), NULL, 0, NULL, NULL); + char* utf8 = new char[utf8Size + 1]; + ZeroMemory(utf8, utf8Size + 1); + WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), utf8, (int)utf8Size, NULL, NULL); + + body.Resize(utf8Size); + memcpy(&body[0], utf8, utf8Size); + delete[] utf8; + } + +/*********************************************************************** +HttpResponse +***********************************************************************/ + + WString HttpResponse::GetBodyUtf8() + { + WString response; + char* utf8 = &body[0]; + vint totalSize = body.Count(); + vint utf16Size = MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, NULL, 0); + wchar_t* utf16 = new wchar_t[utf16Size + 1]; + ZeroMemory(utf16, (utf16Size + 1) * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, utf16, (int)utf16Size); + response = utf16; + delete[] utf16; + return response; + } + +/*********************************************************************** +Utilities +***********************************************************************/ + + struct BufferPair + { + char* buffer; + vint length; + + BufferPair() + :buffer(0) + , length(0) + { + } + + BufferPair(char* _buffer, vint _length) + :buffer(_buffer) + , length(_length) + { + } + + bool operator==(const BufferPair& pair) { return false; } + bool operator!=(const BufferPair& pair) { return true; } + }; + + bool HttpQuery(const HttpRequest& request, HttpResponse& response) + { + // initialize + response.statusCode = -1; + HINTERNET internet = NULL; + HINTERNET connectedInternet = NULL; + HINTERNET requestInternet = NULL; + BOOL httpResult = FALSE; + DWORD error = 0; + List acceptTypes; + List availableBuffers; + + // access http + internet = WinHttpOpen(L"vczh", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); + error = GetLastError(); + if (!internet) goto CLEANUP; + + // connect + connectedInternet = WinHttpConnect(internet, request.server.Buffer(), (int)request.port, 0); + error = GetLastError(); + if (!connectedInternet) goto CLEANUP; + + // open request + for (vint i = 0; i < request.acceptTypes.Count(); i++) + { + acceptTypes.Add(request.acceptTypes.Get(i).Buffer()); + } + acceptTypes.Add(0); + requestInternet = WinHttpOpenRequest(connectedInternet, request.method.Buffer(), request.query.Buffer(), NULL, WINHTTP_NO_REFERER, &acceptTypes[0], (request.secure ? WINHTTP_FLAG_SECURE : 0)); + error = GetLastError(); + if (!requestInternet) goto CLEANUP; + + // authentication, cookie and request + if (request.username != L"" && request.password != L"") + { + WinHttpSetCredentials(requestInternet, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC, request.username.Buffer(), request.password.Buffer(), NULL); + } + if (request.contentType != L"") + { + httpResult = WinHttpAddRequestHeaders(requestInternet, (L"Content-type:" + request.contentType).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); + } + if (request.cookie != L"") + { + WinHttpAddRequestHeaders(requestInternet, (L"Cookie:" + request.cookie).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); + } + + // extra headers + for (int i = 0; i < request.extraHeaders.Count(); i++) + { + WString key = request.extraHeaders.Keys()[i]; + WString value = request.extraHeaders.Values().Get(i); + WinHttpAddRequestHeaders(requestInternet, (key + L":" + value).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); + } + + if (request.body.Count() > 0) + { + httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)&request.body.Get(0), (int)request.body.Count(), (int)request.body.Count(), NULL); + } + else + { + httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, NULL); + } + error = GetLastError(); + if (httpResult == FALSE) goto CLEANUP; + + // receive response + httpResult = WinHttpReceiveResponse(requestInternet, NULL); + error = GetLastError(); + if (httpResult != TRUE) goto CLEANUP; + + // read response status code + { + DWORD headerLength = sizeof(DWORD); + DWORD statusCode = 0; + httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &headerLength, WINHTTP_NO_HEADER_INDEX); + error = GetLastError(); + if (httpResult == FALSE) goto CLEANUP; + response.statusCode = statusCode; + } + // read respons cookie + { + DWORD headerLength = sizeof(DWORD); + httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &headerLength, WINHTTP_NO_HEADER_INDEX); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) + { + wchar_t* rawHeader = new wchar_t[headerLength / sizeof(wchar_t)]; + ZeroMemory(rawHeader, headerLength); + httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, rawHeader, &headerLength, WINHTTP_NO_HEADER_INDEX); + + const wchar_t* cookieStart = wcsstr(rawHeader, L"Cookie:"); + if (cookieStart) + { + const wchar_t* cookieEnd = wcsstr(cookieStart, L";"); + if (cookieEnd) + { + response.cookie = WString::CopyFrom(cookieStart + 7, cookieEnd - cookieStart - 7); + } + } + delete[] rawHeader; + } + } + + // read response body + while (true) + { + DWORD bytesAvailable = 0; + BOOL queryDataAvailableResult = WinHttpQueryDataAvailable(requestInternet, &bytesAvailable); + error = GetLastError(); + if (queryDataAvailableResult == TRUE && bytesAvailable != 0) + { + char* utf8 = new char[bytesAvailable]; + DWORD bytesRead = 0; + BOOL readDataResult = WinHttpReadData(requestInternet, utf8, bytesAvailable, &bytesRead); + error = GetLastError(); + if (readDataResult == TRUE) + { + availableBuffers.Add(BufferPair(utf8, bytesRead)); + } + else + { + delete[] utf8; + } + } + else + { + break; + } + } + + { + // concatincate response body + vint totalSize = 0; + for (auto p : availableBuffers) + { + totalSize += p.length; + } + response.body.Resize(totalSize); + if (totalSize > 0) + { + char* utf8 = new char[totalSize]; + { + char* temp = utf8; + for (auto p : availableBuffers) + { + memcpy(temp, p.buffer, p.length); + temp += p.length; + } + } + memcpy(&response.body[0], utf8, totalSize); + delete[] utf8; + } + for (auto p : availableBuffers) + { + delete[] p.buffer; + } + } + CLEANUP: + if (requestInternet) WinHttpCloseHandle(requestInternet); + if (connectedInternet) WinHttpCloseHandle(connectedInternet); + if (internet) WinHttpCloseHandle(internet); + return response.statusCode != -1; + } + + WString UrlEncodeQuery(const WString& query) + { + vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), NULL, 0, NULL, NULL); + char* utf8 = new char[utf8Size + 1]; + ZeroMemory(utf8, utf8Size + 1); + WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), utf8, (int)utf8Size, NULL, NULL); + + wchar_t* encoded = new wchar_t[utf8Size * 3 + 1]; + ZeroMemory(encoded, (utf8Size * 3 + 1) * sizeof(wchar_t)); + wchar_t* writing = encoded; + for (vint i = 0; i < utf8Size; i++) + { + unsigned char x = (unsigned char)utf8[i]; + if (L'a' <= x && x <= 'z' || L'A' <= x && x <= L'Z' || L'0' <= x && x <= L'9') + { + writing[0] = x; + writing += 1; + } + else + { + writing[0] = L'%'; + writing[1] = L"0123456789ABCDEF"[x / 16]; + writing[2] = L"0123456789ABCDEF"[x % 16]; + writing += 3; + } + } + + WString result = encoded; + delete[] encoded; + delete[] utf8; + return result; + } +} + + +/*********************************************************************** +.\LOCALE.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + using namespace collections; + + extern SYSTEMTIME DateTimeToSystemTime(const DateTime& dateTime); + + BOOL CALLBACK Locale_EnumLocalesProcEx( + _In_ LPWSTR lpLocaleString, + _In_ DWORD dwFlags, + _In_ LPARAM lParam + ) + { + ((List*)lParam)->Add(Locale(lpLocaleString)); + return TRUE; + } + + BOOL CALLBACK Locale_EnumDateFormatsProcExEx( + _In_ LPWSTR lpDateFormatString, + _In_ CALID CalendarID, + _In_ LPARAM lParam + ) + { + ((List*)lParam)->Add(lpDateFormatString); + return TRUE; + } + + BOOL CALLBACK EnumTimeFormatsProcEx( + _In_ LPWSTR lpTimeFormatString, + _In_ LPARAM lParam + ) + { + ((List*)lParam)->Add(lpTimeFormatString); + return TRUE; + } + + WString Transform(const WString& localeName, const WString& input, DWORD flag) + { + int length = LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length() + 1, NULL, 0, NULL, NULL, NULL); + Array buffer(length); + LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length() + 1, &buffer[0], (int)buffer.Count(), NULL, NULL, NULL); + return &buffer[0]; + } + + DWORD TranslateNormalization(Locale::Normalization normalization) + { + DWORD result = 0; + if (normalization & Locale::IgnoreCase) result |= NORM_IGNORECASE; + if (normalization & Locale::IgnoreCaseLinguistic) result |= NORM_IGNORECASE | NORM_LINGUISTIC_CASING; + if (normalization & Locale::IgnoreKanaType) result |= NORM_IGNOREKANATYPE; + if (normalization & Locale::IgnoreNonSpace) result |= NORM_IGNORENONSPACE; + if (normalization & Locale::IgnoreSymbol) result |= NORM_IGNORESYMBOLS; + if (normalization & Locale::IgnoreWidth) result |= NORM_IGNOREWIDTH; + if (normalization & Locale::DigitsAsNumbers) result |= SORT_DIGITSASNUMBERS; + if (normalization & Locale::StringSoft) result |= SORT_STRINGSORT; + return result; + } + +/*********************************************************************** +Locale +***********************************************************************/ + + Locale Locale::Invariant() + { + return Locale(LOCALE_NAME_INVARIANT); + } + + Locale Locale::SystemDefault() + { + wchar_t buffer[LOCALE_NAME_MAX_LENGTH + 1] = { 0 }; + GetSystemDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH); + return Locale(buffer); + } + + Locale Locale::UserDefault() + { + wchar_t buffer[LOCALE_NAME_MAX_LENGTH + 1] = { 0 }; + GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH); + return Locale(buffer); + } + + void Locale::Enumerate(collections::List& locales) + { + EnumSystemLocalesEx(&Locale_EnumLocalesProcEx, LOCALE_ALL, (LPARAM)&locales, NULL); + } + + void Locale::GetShortDateFormats(collections::List& formats)const + { + EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_SHORTDATE, (LPARAM)&formats); + } + + void Locale::GetLongDateFormats(collections::List& formats)const + { + EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_LONGDATE, (LPARAM)&formats); + } + + void Locale::GetYearMonthDateFormats(collections::List& formats)const + { + EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_YEARMONTH, (LPARAM)&formats); + } + + void Locale::GetLongTimeFormats(collections::List& formats)const + { + EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), 0, (LPARAM)&formats); + } + + void Locale::GetShortTimeFormats(collections::List& formats)const + { + EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), TIME_NOSECONDS, (LPARAM)&formats); + } + + WString Locale::FormatDate(const WString& format, DateTime date)const + { + SYSTEMTIME st = DateTimeToSystemTime(date); + int length = GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0, NULL); + if (length == 0) return L""; + Array buffer(length); + GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), &buffer[0], (int)buffer.Count(), NULL); + return &buffer[0]; + } + + WString Locale::FormatTime(const WString& format, DateTime time)const + { + SYSTEMTIME st = DateTimeToSystemTime(time); + int length = GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0); + if (length == 0) return L""; + Array buffer(length); + GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), &buffer[0], (int)buffer.Count()); + return &buffer[0]; + } + + WString Locale::FormatNumber(const WString& number)const + { + int length = GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, NULL, 0); + if (length == 0) return L""; + Array buffer(length); + GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, &buffer[0], (int)buffer.Count()); + return &buffer[0]; + } + + WString Locale::FormatCurrency(const WString& currency)const + { + int length = GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, NULL, 0); + if (length == 0) return L""; + Array buffer(length); + GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, &buffer[0], (int)buffer.Count()); + return &buffer[0]; + } + + WString Locale::GetShortDayOfWeekName(vint dayOfWeek)const + { + return FormatDate(L"ddd", DateTime::FromDateTime(2000, 1, 2 + dayOfWeek)); + } + + WString Locale::GetLongDayOfWeekName(vint dayOfWeek)const + { + return FormatDate(L"dddd", DateTime::FromDateTime(2000, 1, 2 + dayOfWeek)); + } + + WString Locale::GetShortMonthName(vint month)const + { + return FormatDate(L"MMM", DateTime::FromDateTime(2000, month, 1)); + } + + WString Locale::GetLongMonthName(vint month)const + { + return FormatDate(L"MMMM", DateTime::FromDateTime(2000, month, 1)); + } + + WString Locale::ToFullWidth(const WString& str)const + { + return Transform(localeName, str, LCMAP_FULLWIDTH); + } + + WString Locale::ToHalfWidth(const WString& str)const + { + return Transform(localeName, str, LCMAP_HALFWIDTH); + } + + WString Locale::ToHiragana(const WString& str)const + { + return Transform(localeName, str, LCMAP_HIRAGANA); + } + + WString Locale::ToKatagana(const WString& str)const + { + return Transform(localeName, str, LCMAP_KATAKANA); + } + + WString Locale::ToLower(const WString& str)const + { + return Transform(localeName, str, LCMAP_LOWERCASE); + } + + WString Locale::ToUpper(const WString& str)const + { + return Transform(localeName, str, LCMAP_UPPERCASE); + } + + WString Locale::ToLinguisticLower(const WString& str)const + { + return Transform(localeName, str, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING); + } + + WString Locale::ToLinguisticUpper(const WString& str)const + { + return Transform(localeName, str, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING); + } + + WString Locale::ToSimplifiedChinese(const WString& str)const + { + return Transform(localeName, str, LCMAP_SIMPLIFIED_CHINESE); + } + + WString Locale::ToTraditionalChinese(const WString& str)const + { + return Transform(localeName, str, LCMAP_TRADITIONAL_CHINESE); + } + + WString Locale::ToTileCase(const WString& str)const + { + return Transform(localeName, str, LCMAP_TITLECASE); + } + + vint Locale::Compare(const WString& s1, const WString& s2, Normalization normalization)const + { + switch (CompareStringEx(localeName.Buffer(), TranslateNormalization(normalization), s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), NULL, NULL, NULL)) + { + case CSTR_LESS_THAN: return -1; + case CSTR_GREATER_THAN: return 1; + default: return 0; + } + } + + vint Locale::CompareOrdinal(const WString& s1, const WString& s2)const + { + switch (CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), FALSE)) + { + case CSTR_LESS_THAN: return -1; + case CSTR_GREATER_THAN: return 1; + default: return 0; + } + } + + vint Locale::CompareOrdinalIgnoreCase(const WString& s1, const WString& s2)const + { + switch (CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), TRUE)) + { + case CSTR_LESS_THAN: return -1; + case CSTR_GREATER_THAN: return 1; + default: return 0; + } + } + + collections::Pair Locale::FindFirst(const WString& text, const WString& find, Normalization normalization)const + { + int length = 0; + int result = FindNLSStringEx(localeName.Buffer(), FIND_FROMSTART | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL); + return result == -1 ? Pair(-1, 0) : Pair(result, length); + } + + collections::Pair Locale::FindLast(const WString& text, const WString& find, Normalization normalization)const + { + int length = 0; + int result = FindNLSStringEx(localeName.Buffer(), FIND_FROMEND | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL); + return result == -1 ? Pair(-1, 0) : Pair(result, length); + } + + bool Locale::StartsWith(const WString& text, const WString& find, Normalization normalization)const + { + int result = FindNLSStringEx(localeName.Buffer(), FIND_STARTSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL); + return result != -1; + } + + bool Locale::EndsWith(const WString& text, const WString& find, Normalization normalization)const + { + int result = FindNLSStringEx(localeName.Buffer(), FIND_ENDSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL); + return result != -1; + } +} + + +/*********************************************************************** +.\THREADING.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + using namespace threading_internal; + using namespace collections; + +/*********************************************************************** +WaitableObject +***********************************************************************/ + + namespace threading_internal + { + struct WaitableData + { + HANDLE handle; + + WaitableData(HANDLE _handle) + :handle(_handle) + { + } + }; + } + + WaitableObject::WaitableObject() + :waitableData(0) + { + } + + void WaitableObject::SetData(threading_internal::WaitableData* data) + { + waitableData=data; + } + + bool WaitableObject::IsCreated() + { + return waitableData!=0; + } + + bool WaitableObject::Wait() + { + return WaitForTime(INFINITE); + } + + bool WaitableObject::WaitForTime(vint ms) + { + if(IsCreated()) + { + if(WaitForSingleObject(waitableData->handle, (DWORD)ms)==WAIT_OBJECT_0) + { + return true; + } + } + return false; + } + + bool WaitableObject::WaitAll(WaitableObject** objects, vint count) + { + Array handles(count); + for(vint i=0;iwaitableData->handle; + } + DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, INFINITE); + return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0; + + } + + bool WaitableObject::WaitAllForTime(WaitableObject** objects, vint count, vint ms) + { + Array handles(count); + for(vint i=0;iwaitableData->handle; + } + DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, (DWORD)ms); + return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0; + } + + vint WaitableObject::WaitAny(WaitableObject** objects, vint count, bool* abandoned) + { + Array handles(count); + for(vint i=0;iwaitableData->handle; + } + DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, INFINITE); + if(WAIT_OBJECT_0 <= result && result handles(count); + for(vint i=0;iwaitableData->handle; + } + DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, (DWORD)ms); + if(WAIT_OBJECT_0 <= result && resultdeleteAfterStopped; + ThreadLocalStorage::FixStorages(); + try + { + procedure(this, argument); + threadState=Thread::Stopped; + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + throw; + } + if(deleteAfterStopped) + { + delete this; + } + } + public: + ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped) + :procedure(_procedure) + ,argument(_argument) + ,deleteAfterStopped(_deleteAfterStopped) + { + } + }; + + class LambdaThread : public Thread + { + private: + Func procedure; + bool deleteAfterStopped; + + protected: + void Run() + { + bool deleteAfterStopped = this->deleteAfterStopped; + ThreadLocalStorage::FixStorages(); + try + { + procedure(); + threadState=Thread::Stopped; + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + throw; + } + if(deleteAfterStopped) + { + delete this; + } + } + public: + LambdaThread(const Func& _procedure, bool _deleteAfterStopped) + :procedure(_procedure) + ,deleteAfterStopped(_deleteAfterStopped) + { + } + }; + } + + void InternalThreadProc(Thread* thread) + { + thread->Run(); + } + + DWORD WINAPI InternalThreadProcWrapper(LPVOID lpParameter) + { + InternalThreadProc((Thread*)lpParameter); + return 0; + } + + Thread::Thread() + { + internalData=new ThreadData; + internalData->handle=CreateThread(NULL, 0, InternalThreadProcWrapper, this, CREATE_SUSPENDED, &internalData->id); + threadState=Thread::NotStarted; + SetData(internalData); + } + + Thread::~Thread() + { + if (internalData) + { + Stop(); + CloseHandle(internalData->handle); + delete internalData; + } + } + + Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped) + { + if(procedure) + { + Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped); + if(thread->Start()) + { + return thread; + } + else + { + delete thread; + } + } + return 0; + } + + Thread* Thread::CreateAndStart(const Func& procedure, bool deleteAfterStopped) + { + Thread* thread=new LambdaThread(procedure, deleteAfterStopped); + if(thread->Start()) + { + return thread; + } + else + { + delete thread; + } + return 0; + } + + void Thread::Sleep(vint ms) + { + ::Sleep((DWORD)ms); + } + + + vint Thread::GetCPUCount() + { + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; + } + + vint Thread::GetCurrentThreadId() + { + return (vint)::GetCurrentThreadId(); + } + + bool Thread::Start() + { + if(threadState==Thread::NotStarted && internalData->handle!=NULL) + { + if(ResumeThread(internalData->handle)!=-1) + { + threadState=Thread::Running; + return true; + } + } + return false; + } + + bool Thread::Stop() + { + if(internalData->handle!=NULL) + { + if (SuspendThread(internalData->handle) != -1) + { + threadState=Thread::Stopped; + return true; + } + } + return false; + } + + Thread::ThreadState Thread::GetState() + { + return threadState; + } + + void Thread::SetCPU(vint index) + { + SetThreadAffinityMask(internalData->handle, ((vint)1 << index)); + } + +/*********************************************************************** +Mutex +***********************************************************************/ + + namespace threading_internal + { + struct MutexData : public WaitableData + { + MutexData(HANDLE _handle) + :WaitableData(_handle) + { + } + }; + } + + Mutex::Mutex() + :internalData(0) + { + } + + Mutex::~Mutex() + { + if(internalData) + { + CloseHandle(internalData->handle); + delete internalData; + } + } + + bool Mutex::Create(bool owned, const WString& name) + { + if(IsCreated())return false; + BOOL aOwned=owned?TRUE:FALSE; + LPCTSTR aName=name==L""?NULL:name.Buffer(); + HANDLE handle=CreateMutex(NULL, aOwned, aName); + if(handle) + { + internalData=new MutexData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool Mutex::Open(bool inheritable, const WString& name) + { + if(IsCreated())return false; + BOOL aInteritable=inheritable?TRUE:FALSE; + HANDLE handle=OpenMutex(SYNCHRONIZE, aInteritable, name.Buffer()); + if(handle) + { + internalData=new MutexData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool Mutex::Release() + { + if(IsCreated()) + { + return ReleaseMutex(internalData->handle)!=0; + } + return false; + } + +/*********************************************************************** +Semaphore +***********************************************************************/ + + namespace threading_internal + { + struct SemaphoreData : public WaitableData + { + SemaphoreData(HANDLE _handle) + :WaitableData(_handle) + { + } + }; + } + + Semaphore::Semaphore() + :internalData(0) + { + } + + Semaphore::~Semaphore() + { + if(internalData) + { + CloseHandle(internalData->handle); + delete internalData; + } + } + + bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name) + { + if(IsCreated())return false; + LONG aInitial=(LONG)initialCount; + LONG aMax=(LONG)maxCount; + LPCTSTR aName=name==L""?NULL:name.Buffer(); + HANDLE handle=CreateSemaphore(NULL, aInitial, aMax, aName); + if(handle) + { + internalData=new SemaphoreData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool Semaphore::Open(bool inheritable, const WString& name) + { + if(IsCreated())return false; + BOOL aInteritable=inheritable?TRUE:FALSE; + HANDLE handle=OpenSemaphore(SYNCHRONIZE, aInteritable, name.Buffer()); + if(handle) + { + internalData=new SemaphoreData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool Semaphore::Release() + { + if(IsCreated()) + { + return Release(1)!=-1; + } + return false; + } + + vint Semaphore::Release(vint count) + { + if(IsCreated()) + { + LONG previous=-1; + if(ReleaseSemaphore(internalData->handle, (LONG)count, &previous)!=0) + { + return (vint)previous; + } + } + return -1; + } + +/*********************************************************************** +EventObject +***********************************************************************/ + + namespace threading_internal + { + struct EventData : public WaitableData + { + EventData(HANDLE _handle) + :WaitableData(_handle) + { + } + }; + } + + EventObject::EventObject() + :internalData(0) + { + } + + EventObject::~EventObject() + { + if(internalData) + { + CloseHandle(internalData->handle); + delete internalData; + } + } + + bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name) + { + if(IsCreated())return false; + BOOL aSignaled=signaled?TRUE:FALSE; + LPCTSTR aName=name==L""?NULL:name.Buffer(); + HANDLE handle=CreateEvent(NULL, FALSE, aSignaled, aName); + if(handle) + { + internalData=new EventData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool EventObject::CreateManualUnsignal(bool signaled, const WString& name) + { + if(IsCreated())return false; + BOOL aSignaled=signaled?TRUE:FALSE; + LPCTSTR aName=name==L""?NULL:name.Buffer(); + HANDLE handle=CreateEvent(NULL, TRUE, aSignaled, aName); + if(handle) + { + internalData=new EventData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool EventObject::Open(bool inheritable, const WString& name) + { + if(IsCreated())return false; + BOOL aInteritable=inheritable?TRUE:FALSE; + HANDLE handle=OpenEvent(SYNCHRONIZE, aInteritable, name.Buffer()); + if(handle) + { + internalData=new EventData(handle); + SetData(internalData); + } + return IsCreated(); + } + + bool EventObject::Signal() + { + if(IsCreated()) + { + return SetEvent(internalData->handle)!=0; + } + return false; + } + + bool EventObject::Unsignal() + { + if(IsCreated()) + { + return ResetEvent(internalData->handle)!=0; + } + return false; + } + +/*********************************************************************** +ThreadPoolLite +***********************************************************************/ + + struct ThreadPoolQueueProcArgument + { + void(*proc)(void*); + void* argument; + }; + + DWORD WINAPI ThreadPoolQueueProc(void* argument) + { + Ptr proc=(ThreadPoolQueueProcArgument*)argument; + ThreadLocalStorage::FixStorages(); + try + { + proc->proc(proc->argument); + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + } + return 0; + } + + DWORD WINAPI ThreadPoolQueueFunc(void* argument) + { + Ptr> proc=(Func*)argument; + ThreadLocalStorage::FixStorages(); + try + { + (*proc.Obj())(); + ThreadLocalStorage::ClearStorages(); + } + catch (...) + { + ThreadLocalStorage::ClearStorages(); + } + return 0; + } + + ThreadPoolLite::ThreadPoolLite() + { + } + + ThreadPoolLite::~ThreadPoolLite() + { + } + + bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument) + { + ThreadPoolQueueProcArgument* p=new ThreadPoolQueueProcArgument; + p->proc=proc; + p->argument=argument; + if(QueueUserWorkItem(&ThreadPoolQueueProc, p, WT_EXECUTEDEFAULT)) + { + return true; + } + else + { + delete p; + return false; + } + } + + bool ThreadPoolLite::Queue(const Func& proc) + { + Func* p=new Func(proc); + if(QueueUserWorkItem(&ThreadPoolQueueFunc, p, WT_EXECUTEDEFAULT)) + { + return true; + } + else + { + delete p; + return false; + } + } + +/*********************************************************************** +CriticalSection +***********************************************************************/ + + namespace threading_internal + { + struct CriticalSectionData + { + CRITICAL_SECTION criticalSection; + }; + } + + CriticalSection::Scope::Scope(CriticalSection& _criticalSection) + :criticalSection(&_criticalSection) + { + criticalSection->Enter(); + } + + CriticalSection::Scope::~Scope() + { + criticalSection->Leave(); + } + + CriticalSection::CriticalSection() + { + internalData=new CriticalSectionData; + InitializeCriticalSection(&internalData->criticalSection); + } + + CriticalSection::~CriticalSection() + { + DeleteCriticalSection(&internalData->criticalSection); + delete internalData; + } + + bool CriticalSection::TryEnter() + { + return TryEnterCriticalSection(&internalData->criticalSection)!=0; + } + + void CriticalSection::Enter() + { + EnterCriticalSection(&internalData->criticalSection); + } + + void CriticalSection::Leave() + { + LeaveCriticalSection(&internalData->criticalSection); + } + +/*********************************************************************** +ReaderWriterLock +***********************************************************************/ + + namespace threading_internal + { + struct ReaderWriterLockData + { + SRWLOCK lock; + }; + } + + ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock) + :lock(&_lock) + { + lock->EnterReader(); + } + + ReaderWriterLock::ReaderScope::~ReaderScope() + { + lock->LeaveReader(); + } + + ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock) + :lock(&_lock) + { + lock->EnterWriter(); + } + + ReaderWriterLock::WriterScope::~WriterScope() + { + lock->LeaveWriter(); + } + + ReaderWriterLock::ReaderWriterLock() + :internalData(new threading_internal::ReaderWriterLockData) + { + InitializeSRWLock(&internalData->lock); + } + + ReaderWriterLock::~ReaderWriterLock() + { + delete internalData; + } + + bool ReaderWriterLock::TryEnterReader() + { + return TryAcquireSRWLockShared(&internalData->lock)!=0; + } + + void ReaderWriterLock::EnterReader() + { + AcquireSRWLockShared(&internalData->lock); + } + + void ReaderWriterLock::LeaveReader() + { + ReleaseSRWLockShared(&internalData->lock); + } + + bool ReaderWriterLock::TryEnterWriter() + { + return TryAcquireSRWLockExclusive(&internalData->lock)!=0; + } + + void ReaderWriterLock::EnterWriter() + { + AcquireSRWLockExclusive(&internalData->lock); + } + + void ReaderWriterLock::LeaveWriter() + { + ReleaseSRWLockExclusive(&internalData->lock); + } + +/*********************************************************************** +ConditionVariable +***********************************************************************/ + + namespace threading_internal + { + struct ConditionVariableData + { + CONDITION_VARIABLE variable; + }; + } + + ConditionVariable::ConditionVariable() + :internalData(new threading_internal::ConditionVariableData) + { + InitializeConditionVariable(&internalData->variable); + } + + ConditionVariable::~ConditionVariable() + { + delete internalData; + } + + bool ConditionVariable::SleepWith(CriticalSection& cs) + { + return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, INFINITE)!=0; + } + + bool ConditionVariable::SleepWithForTime(CriticalSection& cs, vint ms) + { + return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, (DWORD)ms)!=0; + } + + bool ConditionVariable::SleepWithReader(ReaderWriterLock& lock) + { + return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0; + } + + bool ConditionVariable::SleepWithReaderForTime(ReaderWriterLock& lock, vint ms) + { + return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0; + } + + bool ConditionVariable::SleepWithWriter(ReaderWriterLock& lock) + { + return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, 0)!=0; + } + + bool ConditionVariable::SleepWithWriterForTime(ReaderWriterLock& lock, vint ms) + { + return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, 0)!=0; + } + + void ConditionVariable::WakeOnePending() + { + WakeConditionVariable(&internalData->variable); + } + + void ConditionVariable::WakeAllPendings() + { + WakeAllConditionVariable(&internalData->variable); + } + +/*********************************************************************** +SpinLock +***********************************************************************/ + + SpinLock::Scope::Scope(SpinLock& _spinLock) + :spinLock(&_spinLock) + { + spinLock->Enter(); + } + + SpinLock::Scope::~Scope() + { + spinLock->Leave(); + } + + SpinLock::SpinLock() + :token(0) + { + } + + SpinLock::~SpinLock() + { + } + + bool SpinLock::TryEnter() + { + return _InterlockedExchange(&token, 1)==0; + } + + void SpinLock::Enter() + { + while(_InterlockedCompareExchange(&token, 1, 0)!=0) + { + while(token!=0) _mm_pause(); + } + } + + void SpinLock::Leave() + { + _InterlockedExchange(&token, 0); + } + +/*********************************************************************** +ThreadLocalStorage +***********************************************************************/ + +#define KEY ((DWORD&)key) + + ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor) + :destructor(_destructor) + { + static_assert(sizeof(key) >= sizeof(DWORD), "ThreadLocalStorage's key storage is not large enouth."); + PushStorage(this); + KEY = TlsAlloc(); + CHECK_ERROR(KEY != TLS_OUT_OF_INDEXES, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to alloc new thread local storage index."); + } + + ThreadLocalStorage::~ThreadLocalStorage() + { + TlsFree(KEY); + } + + void* ThreadLocalStorage::Get() + { + CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage."); + return TlsGetValue(KEY); + } + + void ThreadLocalStorage::Set(void* data) + { + CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage."); + TlsSetValue(KEY, data); + } + +#undef KEY +} + + +/*********************************************************************** +.\STREAM\CHARFORMAT.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + +#include + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + namespace stream + { + bool IsMbcsLeadByte(char c) + { + return IsDBCSLeadByte(c); + } + +/*********************************************************************** +Mbcs +***********************************************************************/ + + vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) + { + vint length = WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL); + char* mbcs = new char[length]; + WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL); + vint result = stream->Write(mbcs, length); + delete[] mbcs; + + if (result != length) + { + Close(); + return 0; + } + return chars; + } + + void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars) + { + MultiByteToWideChar(CP_THREAD_ACP, 0, mbcsBuffer, (int)mbcsChars, wideBuffer, (int)wideChars); + } + +/*********************************************************************** +Utf8 +***********************************************************************/ + + vint Utf8Encoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) + { + vint length = WideCharToMultiByte(CP_UTF8, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL); + char* mbcs = new char[length]; + WideCharToMultiByte(CP_UTF8, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL); + vint result = stream->Write(mbcs, length); + delete[] mbcs; + if (result != length) + { + Close(); + return 0; + } + return chars; + } + } +} + + +/*********************************************************************** +.\STREAM\CHARFORMAT_TESTENCODING.WINDOWS.CPP +***********************************************************************/ +/*********************************************************************** +Author: Zihan Chen (vczh) +Licensed under https://github.com/vczh-libraries/License +***********************************************************************/ + + +#ifndef VCZH_MSVC +static_assert(false, "Do not build this file for non-Windows applications."); +#endif + +namespace vl +{ + namespace stream + { +/*********************************************************************** +Helper Functions +***********************************************************************/ + + extern bool CanBeMbcs(unsigned char* buffer, vint size); + extern bool CanBeUtf8(unsigned char* buffer, vint size); + extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs); + extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs); + + template + bool GetEncodingResult(int(&tests)[Count], bool(&results)[Count], int test) + { + for (vint i = 0; i < Count; i++) + { + if (tests[i] & test) + { + if (results[i]) return true; + } + } + return false; + } + +/*********************************************************************** +TestEncoding +***********************************************************************/ + + extern void TestEncodingInternal( + unsigned char* buffer, + vint size, + BomEncoder::Encoding& encoding, + bool containsBom, + bool utf16HitSurrogatePairs, + bool utf16BEHitSurrogatePairs, + bool roughMbcs, + bool roughUtf8, + bool roughUtf16, + bool roughUtf16BE + ) + { + int tests[] = + { + IS_TEXT_UNICODE_REVERSE_ASCII16, + IS_TEXT_UNICODE_REVERSE_STATISTICS, + IS_TEXT_UNICODE_REVERSE_CONTROLS, + + IS_TEXT_UNICODE_ASCII16, + IS_TEXT_UNICODE_STATISTICS, + IS_TEXT_UNICODE_CONTROLS, + + IS_TEXT_UNICODE_ILLEGAL_CHARS, + IS_TEXT_UNICODE_ODD_LENGTH, + IS_TEXT_UNICODE_NULL_BYTES, + }; + + const vint TestCount = sizeof(tests) / sizeof(*tests); + bool results[TestCount]; + for (vint i = 0; i < TestCount; i++) + { + int test = tests[i]; + results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; + } + + if (size % 2 == 0 + && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16) + && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_STATISTICS) + && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS) + ) + { + for (vint i = 0; i < size; i += 2) + { + unsigned char c = buffer[i]; + buffer[i] = buffer[i + 1]; + buffer[i + 1] = c; + } + // 3 = (count of reverse group) = (count of unicode group) + for (vint i = 0; i < 3; i++) + { + int test = tests[i + 3]; + results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; + } + for (vint i = 0; i < size; i += 2) + { + unsigned char c = buffer[i]; + buffer[i] = buffer[i + 1]; + buffer[i + 1] = c; + } + } + + if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) + { + if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_ASCII_MASK)) + { + encoding = BomEncoder::Utf8; + } + else if (roughUtf8 || !roughMbcs) + { + encoding = BomEncoder::Utf8; + } + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_ASCII16)) + { + encoding = BomEncoder::Utf16; + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16)) + { + encoding = BomEncoder::Utf16BE; + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_CONTROLS)) + { + encoding = BomEncoder::Utf16; + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS)) + { + encoding = BomEncoder::Utf16BE; + } + else + { + if (!roughUtf8) + { + if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) + { + encoding = BomEncoder::Utf16; + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) + { + encoding = BomEncoder::Utf16BE; + } + } + else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) + { + encoding = BomEncoder::Utf8; + } + else if (roughUtf8 || !roughMbcs) + { + encoding = BomEncoder::Utf8; + } + } + } + } +} + diff --git a/Import/VlppOS.cpp b/Import/VlppOS.cpp index c71bf848..ed54c62a 100644 --- a/Import/VlppOS.cpp +++ b/Import/VlppOS.cpp @@ -12,15 +12,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#include -#include -#pragma comment(lib, "Shlwapi.lib") -#elif defined VCZH_GCC -#include -#include -#include -#endif namespace vl { @@ -35,117 +26,25 @@ namespace vl FilePath ***********************************************************************/ -#if defined VCZH_GCC - const wchar_t FilePath::Delimiter; -#endif - - void FilePath::Initialize() + void FilePath::NormalizeDelimiters(collections::Array& buffer) { + for (vint i = 0; i < buffer.Count(); i++) { - Array buffer(fullPath.Length() + 1); -#if defined VCZH_MSVC - wcscpy_s(&buffer[0], fullPath.Length() + 1, fullPath.Buffer()); -#elif defined VCZH_GCC - wcscpy(&buffer[0], fullPath.Buffer()); -#endif - for (vint i = 0; i < buffer.Count(); i++) + if (buffer[i] == L'\\' || buffer[i] == L'/') { - if (buffer[i] == L'\\' || buffer[i] == L'/') - { - buffer[i] = Delimiter; - } - } - fullPath = &buffer[0]; - } - -#if defined VCZH_MSVC - if (fullPath != L"") - { - if (fullPath.Length() < 2 || fullPath[1] != L':') - { - wchar_t buffer[MAX_PATH + 1] = { 0 }; - auto result = GetCurrentDirectory(sizeof(buffer) / sizeof(*buffer), buffer); - if (result > MAX_PATH + 1 || result == 0) - { - throw ArgumentException(L"Failed to call GetCurrentDirectory.", L"vl::filesystem::FilePath::Initialize", L""); - } - fullPath = WString(buffer) + L"\\" + fullPath; - } - { - wchar_t buffer[MAX_PATH + 1] = { 0 }; - if (fullPath.Length() == 2 && fullPath[1] == L':') - { - fullPath += L"\\"; - } - auto result = GetFullPathName(fullPath.Buffer(), sizeof(buffer) / sizeof(*buffer), buffer, NULL); - if (result > MAX_PATH + 1 || result == 0) - { - throw ArgumentException(L"The path is illegal.", L"vl::filesystem::FilePath::FilePath", L"_filePath"); - } - - { - wchar_t shortPath[MAX_PATH + 1]; - wchar_t longPath[MAX_PATH + 1]; - if (GetShortPathName(buffer, shortPath, MAX_PATH) > 0) - { - if (GetLongPathName(shortPath, longPath, MAX_PATH) > 0) - { - memcpy(buffer, longPath, sizeof(buffer)); - } - } - } - fullPath = buffer; + buffer[i] = Delimiter; } } -#elif defined VCZH_GCC - if (fullPath.Length() == 0) - fullPath = L"/"; + } - if (fullPath[0] != Delimiter) - { - char buffer[PATH_MAX] = { 0 }; - getcwd(buffer, PATH_MAX); - fullPath = atow(AString(buffer)) + WString::FromChar(Delimiter) + fullPath; - } - - { - collections::List components; - GetPathComponents(fullPath, components); - for(int i = 0; i < components.Count(); i++) - { - if(components[i] == L".") - { - components.RemoveAt(i); - i--; - } - else if(components[i] == L"..") - { - if(i > 0) - { - components.RemoveAt(i); - components.RemoveAt(i - 1); - i -= 2; - } - else - { - throw ArgumentException(L"Illegal path."); - } - } - } - - fullPath = ComponentsToPath(components); - } -#endif + void FilePath::TrimLastDelimiter(WString& fullPath) + { if (fullPath != L"/" && fullPath.Length() > 0 && fullPath[fullPath.Length() - 1] == Delimiter) { fullPath = fullPath.Left(fullPath.Length() - 1); } } - FilePath::FilePath() - { - } - FilePath::FilePath(const WString& _filePath) :fullPath(_filePath) { @@ -163,10 +62,6 @@ FilePath { } - FilePath::~FilePath() - { - } - vint FilePath::Compare(const FilePath& a, const FilePath& b) { return (vint)WString::Compare(a.fullPath, b.fullPath); @@ -184,47 +79,6 @@ FilePath } } - bool FilePath::IsFile()const - { -#if defined VCZH_MSVC - WIN32_FILE_ATTRIBUTE_DATA info; - BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); - if (!result) return false; - return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; -#elif defined VCZH_GCC - struct stat info; - AString path = wtoa(fullPath); - int result = stat(path.Buffer(), &info); - if(result != 0) return false; - else return S_ISREG(info.st_mode); -#endif - } - - bool FilePath::IsFolder()const - { -#if defined VCZH_MSVC - WIN32_FILE_ATTRIBUTE_DATA info; - BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info); - if (!result) return false; - return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -#elif defined VCZH_GCC - struct stat info; - AString path = wtoa(fullPath); - int result = stat(path.Buffer(), &info); - if(result != 0) return false; - else return S_ISDIR(info.st_mode); -#endif - } - - bool FilePath::IsRoot()const - { -#if defined VCZH_MSVC - return fullPath == L""; -#elif defined VCZH_GCC - return fullPath == L"/"; -#endif - } - WString FilePath::GetName()const { auto delimiter = WString::FromChar(Delimiter); @@ -246,53 +100,6 @@ FilePath return fullPath; } - WString FilePath::GetRelativePathFor(const FilePath& _filePath) - { - if (fullPath.Length()==0 || _filePath.fullPath.Length()==0 || fullPath[0] != _filePath.fullPath[0]) - { - return _filePath.fullPath; - } -#if defined VCZH_MSVC - wchar_t buffer[MAX_PATH + 1] = { 0 }; - PathRelativePathTo( - buffer, - fullPath.Buffer(), - (IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0), - _filePath.fullPath.Buffer(), - (_filePath.IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0) - ); - return buffer; -#elif defined VCZH_GCC - collections::List srcComponents, tgtComponents, resultComponents; - GetPathComponents(IsFolder() ? fullPath : GetFolder().GetFullPath(), srcComponents); - GetPathComponents(_filePath.fullPath, tgtComponents); - - int minLength = srcComponents.Count() <= tgtComponents.Count() ? srcComponents.Count() : tgtComponents.Count(); - int lastCommonComponent = 0; - for(int i = 0; i < minLength; i++) - { - if(srcComponents[i] == tgtComponents[i]) - { - lastCommonComponent = i; - } - else - break; - } - - for(int i = lastCommonComponent + 1; i < srcComponents.Count(); i++) - { - resultComponents.Add(L".."); - } - - for(int i = lastCommonComponent + 1; i < tgtComponents.Count(); i++) - { - resultComponents.Add(tgtComponents[i]); - } - - return ComponentsToPath(resultComponents); -#endif - } - void FilePath::GetPathComponents(WString path, collections::List& components) { WString pathRemaining = path; @@ -300,35 +107,35 @@ FilePath components.Clear(); - while(true) + while (true) { auto index = INVLOC.FindFirst(pathRemaining, delimiter, Locale::None); if (index.key == -1) break; - if(index.key != 0) + if (index.key != 0) components.Add(pathRemaining.Left(index.key)); else { -#if defined VCZH_GCC - // Unix absolute path starting with "/" - // components[0] will be L"/" - components.Add(delimiter); -#elif defined VCZH_MSVC - if(pathRemaining.Length() >= 2 && pathRemaining[1] == Delimiter) +#if defined VCZH_MSVC + if (pathRemaining.Length() >= 2 && pathRemaining[1] == Delimiter) { // Windows UNC Path starting with "\\" // components[0] will be L"\\" components.Add(L"\\"); index.value++; } +#elif defined VCZH_GCC + // Unix absolute path starting with "/" + // components[0] will be L"/" + components.Add(delimiter); #endif } pathRemaining = pathRemaining.Right(pathRemaining.Length() - (index.key + index.value)); } - if(pathRemaining.Length() != 0) + if (pathRemaining.Length() != 0) { components.Add(pathRemaining); } @@ -340,17 +147,17 @@ FilePath auto delimiter = WString::FromChar(Delimiter); int i = 0; - -#if defined VCZH_GCC - // For Unix-like OSes, if first component is "/" then take it as absolute path - if(components.Count() > 0 && components[0] == delimiter) + +#if defined VCZH_MSVC + // For Windows, if first component is "\\" then it is an UNC path + if(components.Count() > 0 && components[0] == L"\\") { result += delimiter; i++; } -#elif defined VCZH_MSVC - // For Windows, if first component is "\\" then it is an UNC path - if(components.Count() > 0 && components[0] == L"\\") +#elif defined VCZH_GCC + // For Unix-like OSes, if first component is "/" then take it as absolute path + if(components.Count() > 0 && components[0] == delimiter) { result += delimiter; i++; @@ -370,20 +177,12 @@ FilePath /*********************************************************************** File ***********************************************************************/ - - File::File() - { - } File::File(const FilePath& _filePath) :filePath(_filePath) { } - File::~File() - { - } - const FilePath& File::GetFilePath()const { return filePath; @@ -567,211 +366,20 @@ File return filePath.IsFile(); } - bool File::Delete()const - { -#if defined VCZH_MSVC - return DeleteFile(filePath.GetFullPath().Buffer()) != 0; -#elif defined VCZH_GCC - AString path = wtoa(filePath.GetFullPath()); - return unlink(path.Buffer()) == 0; -#endif - } - - bool File::Rename(const WString& newName)const - { -#if defined VCZH_MSVC - WString oldFileName = filePath.GetFullPath(); - WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); - return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; -#elif defined VCZH_GCC - AString oldFileName = wtoa(filePath.GetFullPath()); - AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); - return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; -#endif - } - /*********************************************************************** Folder ***********************************************************************/ - - Folder::Folder() - { - } Folder::Folder(const FilePath& _filePath) :filePath(_filePath) { } - Folder::~Folder() - { - } - const FilePath& Folder::GetFilePath()const { return filePath; } - bool Folder::GetFolders(collections::List& folders)const - { -#if defined VCZH_MSVC - if (filePath.IsRoot()) - { - auto bufferSize = GetLogicalDriveStrings(0, nullptr); - if (bufferSize > 0) - { - Array buffer(bufferSize); - if (GetLogicalDriveStrings((DWORD)buffer.Count(), &buffer[0]) > 0) - { - auto begin = &buffer[0]; - auto end = begin + buffer.Count(); - while (begin < end && *begin) - { - WString driveString = begin; - begin += driveString.Length() + 1; - folders.Add(Folder(FilePath(driveString))); - } - return true; - } - } - return false; - } - else - { - if (!Exists()) return false; - WIN32_FIND_DATA findData; - HANDLE findHandle = INVALID_HANDLE_VALUE; - - while (true) - { - if (findHandle == INVALID_HANDLE_VALUE) - { - WString searchPath = (filePath / L"*").GetFullPath(); - findHandle = FindFirstFile(searchPath.Buffer(), &findData); - if (findHandle == INVALID_HANDLE_VALUE) - { - break; - } - } - else - { - BOOL result = FindNextFile(findHandle, &findData); - if (result == 0) - { - FindClose(findHandle); - break; - } - } - - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0) - { - folders.Add(Folder(filePath / findData.cFileName)); - } - } - } - return true; - } -#elif defined VCZH_GCC - if (!Exists()) return false; - - DIR *dir; - AString searchPath = wtoa(filePath.GetFullPath()); - - if ((dir = opendir(searchPath.Buffer())) == NULL) - { - return false; - } - - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) - { - WString childName = atow(AString(entry->d_name)); - FilePath childFullPath = filePath / childName; - if (childName != L"." && childName != L".." && childFullPath.IsFolder()) - { - folders.Add(Folder(childFullPath)); - } - } - - if (closedir(dir) != 0) - { - return false; - } - - return true; -#endif - } - - bool Folder::GetFiles(collections::List& files)const - { -#if defined VCZH_MSVC - if (filePath.IsRoot()) - { - return true; - } - if (!Exists()) return false; - WIN32_FIND_DATA findData; - HANDLE findHandle = INVALID_HANDLE_VALUE; - - while (true) - { - if (findHandle == INVALID_HANDLE_VALUE) - { - WString searchPath = (filePath / L"*").GetFullPath(); - findHandle = FindFirstFile(searchPath.Buffer(), &findData); - if (findHandle == INVALID_HANDLE_VALUE) - { - break; - } - } - else - { - BOOL result = FindNextFile(findHandle, &findData); - if (result == 0) - { - FindClose(findHandle); - break; - } - } - - if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - files.Add(File(filePath / findData.cFileName)); - } - } - return true; -#elif defined VCZH_GCC - if (!Exists()) return false; - - DIR *dir; - AString searchPath = wtoa(filePath.GetFullPath()); - - if ((dir = opendir(searchPath.Buffer())) == NULL) - { - return false; - } - - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) - { - FilePath childFullPath = filePath / (atow(AString(entry->d_name))); - if (childFullPath.IsFile()) - { - files.Add(File(childFullPath)); - } - } - - if (closedir(dir) != 0) - { - return false; - } - - return true; -#endif - } - bool Folder::Exists()const { return filePath.IsFolder(); @@ -788,12 +396,7 @@ Folder } else { -#if defined VCZH_MSVC - return CreateDirectory(filePath.GetFullPath().Buffer(), NULL) != 0; -#elif defined VCZH_GCC - AString path = wtoa(filePath.GetFullPath()); - return mkdir(path.Buffer(), 0777) == 0; -#endif + return CreateNonRecursively(); } } @@ -819,375 +422,12 @@ Folder return Delete(false); } -#if defined VCZH_MSVC - return RemoveDirectory(filePath.GetFullPath().Buffer()) != 0; -#elif defined VCZH_GCC - AString path = wtoa(filePath.GetFullPath()); - return rmdir(path.Buffer()) == 0; -#endif - } - - bool Folder::Rename(const WString& newName)const - { -#if defined VCZH_MSVC - WString oldFileName = filePath.GetFullPath(); - WString newFileName = (filePath.GetFolder() / newName).GetFullPath(); - return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0; -#elif defined VCZH_GCC - AString oldFileName = wtoa(filePath.GetFullPath()); - AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath()); - return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0; -#endif + return DeleteNonRecursively(); } } } -/*********************************************************************** -.\HTTPUTILITY.CPP -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - - -#ifdef VCZH_MSVC -#include - -#pragma comment(lib, "WinHttp.lib") - -namespace vl -{ - using namespace collections; - -/*********************************************************************** -HttpRequest -***********************************************************************/ - - bool HttpRequest::SetHost(const WString& inputQuery) - { - if (method == L"") - { - method = L"GET"; - } - - server = L""; - query = L""; - port = 0; - secure = false; - - { - if (server == L"") - { - if (inputQuery.Length() > 7) - { - WString protocol = inputQuery.Sub(0, 8); - if (_wcsicmp(protocol.Buffer(), L"https://") == 0) - { - const wchar_t* reading = inputQuery.Buffer() + 8; - const wchar_t* index1 = wcschr(reading, L':'); - const wchar_t* index2 = wcschr(reading, L'/'); - if (index2) - { - query = index2; - server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading); - port = INTERNET_DEFAULT_HTTPS_PORT; - secure = true; - if (index1) - { - auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1); - port = _wtoi(portString.Buffer()); - } - return true; - } - } - } - } - if (server == L"") - { - if (inputQuery.Length() > 6) - { - WString protocol = inputQuery.Sub(0, 7); - if (_wcsicmp(protocol.Buffer(), L"http://") == 0) - { - const wchar_t* reading = inputQuery.Buffer() + 7; - const wchar_t* index1 = wcschr(reading, L':'); - const wchar_t* index2 = wcschr(reading, L'/'); - if (index2) - { - query = index2; - server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading); - port = INTERNET_DEFAULT_HTTP_PORT; - if (index1) - { - auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1); - port = _wtoi(portString.Buffer()); - } - return true; - } - } - } - } - } - return false; - } - - void HttpRequest::SetBodyUtf8(const WString& bodyString) - { - vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), NULL, 0, NULL, NULL); - char* utf8 = new char[utf8Size + 1]; - ZeroMemory(utf8, utf8Size + 1); - WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), utf8, (int)utf8Size, NULL, NULL); - - body.Resize(utf8Size); - memcpy(&body[0], utf8, utf8Size); - delete[] utf8; - } - -/*********************************************************************** -HttpResponse -***********************************************************************/ - - WString HttpResponse::GetBodyUtf8() - { - WString response; - char* utf8 = &body[0]; - vint totalSize = body.Count(); - vint utf16Size = MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, NULL, 0); - wchar_t* utf16 = new wchar_t[utf16Size + 1]; - ZeroMemory(utf16, (utf16Size + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, utf16, (int)utf16Size); - response = utf16; - delete[] utf16; - return response; - } - -/*********************************************************************** -Utilities -***********************************************************************/ - - struct BufferPair - { - char* buffer; - vint length; - - BufferPair() - :buffer(0) - , length(0) - { - } - - BufferPair(char* _buffer, vint _length) - :buffer(_buffer) - , length(_length) - { - } - - bool operator==(const BufferPair& pair) { return false; } - bool operator!=(const BufferPair& pair) { return true; } - }; - - bool HttpQuery(const HttpRequest& request, HttpResponse& response) - { - // initialize - response.statusCode = -1; - HINTERNET internet = NULL; - HINTERNET connectedInternet = NULL; - HINTERNET requestInternet = NULL; - BOOL httpResult = FALSE; - DWORD error = 0; - List acceptTypes; - List availableBuffers; - - // access http - internet = WinHttpOpen(L"vczh", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); - error = GetLastError(); - if (!internet) goto CLEANUP; - - // connect - connectedInternet = WinHttpConnect(internet, request.server.Buffer(), (int)request.port, 0); - error = GetLastError(); - if (!connectedInternet) goto CLEANUP; - - // open request - for (vint i = 0; i < request.acceptTypes.Count(); i++) - { - acceptTypes.Add(request.acceptTypes.Get(i).Buffer()); - } - acceptTypes.Add(0); - requestInternet = WinHttpOpenRequest(connectedInternet, request.method.Buffer(), request.query.Buffer(), NULL, WINHTTP_NO_REFERER, &acceptTypes[0], (request.secure ? WINHTTP_FLAG_SECURE : 0)); - error = GetLastError(); - if (!requestInternet) goto CLEANUP; - - // authentication, cookie and request - if (request.username != L"" && request.password != L"") - { - WinHttpSetCredentials(requestInternet, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC, request.username.Buffer(), request.password.Buffer(), NULL); - } - if (request.contentType != L"") - { - httpResult = WinHttpAddRequestHeaders(requestInternet, (L"Content-type:" + request.contentType).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); - } - if (request.cookie != L"") - { - WinHttpAddRequestHeaders(requestInternet, (L"Cookie:" + request.cookie).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); - } - - // extra headers - for (int i = 0; i < request.extraHeaders.Count(); i++) - { - WString key = request.extraHeaders.Keys()[i]; - WString value = request.extraHeaders.Values().Get(i); - WinHttpAddRequestHeaders(requestInternet, (key + L":" + value).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD); - } - - if (request.body.Count() > 0) - { - httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)&request.body.Get(0), (int)request.body.Count(), (int)request.body.Count(), NULL); - } - else - { - httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, NULL); - } - error = GetLastError(); - if (httpResult == FALSE) goto CLEANUP; - - // receive response - httpResult = WinHttpReceiveResponse(requestInternet, NULL); - error = GetLastError(); - if (httpResult != TRUE) goto CLEANUP; - - // read response status code - { - DWORD headerLength = sizeof(DWORD); - DWORD statusCode = 0; - httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &headerLength, WINHTTP_NO_HEADER_INDEX); - error = GetLastError(); - if (httpResult == FALSE) goto CLEANUP; - response.statusCode = statusCode; - } - // read respons cookie - { - DWORD headerLength = sizeof(DWORD); - httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &headerLength, WINHTTP_NO_HEADER_INDEX); - error = GetLastError(); - if (error == ERROR_INSUFFICIENT_BUFFER) - { - wchar_t* rawHeader = new wchar_t[headerLength / sizeof(wchar_t)]; - ZeroMemory(rawHeader, headerLength); - httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, rawHeader, &headerLength, WINHTTP_NO_HEADER_INDEX); - - const wchar_t* cookieStart = wcsstr(rawHeader, L"Cookie:"); - if (cookieStart) - { - const wchar_t* cookieEnd = wcsstr(cookieStart, L";"); - if (cookieEnd) - { - response.cookie = WString::CopyFrom(cookieStart + 7, cookieEnd - cookieStart - 7); - } - } - delete[] rawHeader; - } - } - - // read response body - while (true) - { - DWORD bytesAvailable = 0; - BOOL queryDataAvailableResult = WinHttpQueryDataAvailable(requestInternet, &bytesAvailable); - error = GetLastError(); - if (queryDataAvailableResult == TRUE && bytesAvailable != 0) - { - char* utf8 = new char[bytesAvailable]; - DWORD bytesRead = 0; - BOOL readDataResult = WinHttpReadData(requestInternet, utf8, bytesAvailable, &bytesRead); - error = GetLastError(); - if (readDataResult == TRUE) - { - availableBuffers.Add(BufferPair(utf8, bytesRead)); - } - else - { - delete[] utf8; - } - } - else - { - break; - } - } - - { - // concatincate response body - vint totalSize = 0; - for (auto p : availableBuffers) - { - totalSize += p.length; - } - response.body.Resize(totalSize); - if (totalSize > 0) - { - char* utf8 = new char[totalSize]; - { - char* temp = utf8; - for (auto p : availableBuffers) - { - memcpy(temp, p.buffer, p.length); - temp += p.length; - } - } - memcpy(&response.body[0], utf8, totalSize); - delete[] utf8; - } - for (auto p : availableBuffers) - { - delete[] p.buffer; - } - } - CLEANUP: - if (requestInternet) WinHttpCloseHandle(requestInternet); - if (connectedInternet) WinHttpCloseHandle(connectedInternet); - if (internet) WinHttpCloseHandle(internet); - return response.statusCode != -1; - } - - WString UrlEncodeQuery(const WString& query) - { - vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), NULL, 0, NULL, NULL); - char* utf8 = new char[utf8Size + 1]; - ZeroMemory(utf8, utf8Size + 1); - WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), utf8, (int)utf8Size, NULL, NULL); - - wchar_t* encoded = new wchar_t[utf8Size * 3 + 1]; - ZeroMemory(encoded, (utf8Size * 3 + 1) * sizeof(wchar_t)); - wchar_t* writing = encoded; - for (vint i = 0; i < utf8Size; i++) - { - unsigned char x = (unsigned char)utf8[i]; - if (L'a' <= x && x <= 'z' || L'A' <= x && x <= L'Z' || L'0' <= x && x <= L'9') - { - writing[0] = x; - writing += 1; - } - else - { - writing[0] = L'%'; - writing[1] = L"0123456789ABCDEF"[x / 16]; - writing[2] = L"0123456789ABCDEF"[x % 16]; - writing += 3; - } - } - - WString result = encoded; - delete[] encoded; - delete[] utf8; - return result; - } -} -#endif - - /*********************************************************************** .\LOCALE.CPP ***********************************************************************/ @@ -1196,73 +436,9 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#elif defined VCZH_GCC -#include -#include -#include -#endif namespace vl { - using namespace collections; - -#if defined VCZH_MSVC - - extern SYSTEMTIME DateTimeToSystemTime(const DateTime& dateTime); - - BOOL CALLBACK Locale_EnumLocalesProcEx( - _In_ LPWSTR lpLocaleString, - _In_ DWORD dwFlags, - _In_ LPARAM lParam - ) - { - ((List*)lParam)->Add(Locale(lpLocaleString)); - return TRUE; - } - - BOOL CALLBACK Locale_EnumDateFormatsProcExEx( - _In_ LPWSTR lpDateFormatString, - _In_ CALID CalendarID, - _In_ LPARAM lParam - ) - { - ((List*)lParam)->Add(lpDateFormatString); - return TRUE; - } - - BOOL CALLBACK EnumTimeFormatsProcEx( - _In_ LPWSTR lpTimeFormatString, - _In_ LPARAM lParam - ) - { - ((List*)lParam)->Add(lpTimeFormatString); - return TRUE; - } - - WString Transform(const WString& localeName, const WString& input, DWORD flag) - { - int length=LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length()+1, NULL, 0, NULL, NULL, NULL); - Array buffer(length); - LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length()+1, &buffer[0], (int)buffer.Count(), NULL, NULL, NULL); - return &buffer[0]; - } - - DWORD TranslateNormalization(Locale::Normalization normalization) - { - DWORD result=0; - if(normalization&Locale::IgnoreCase) result|=NORM_IGNORECASE; - if(normalization&Locale::IgnoreCaseLinguistic) result|=NORM_IGNORECASE | NORM_LINGUISTIC_CASING; - if(normalization&Locale::IgnoreKanaType) result|=NORM_IGNOREKANATYPE; - if(normalization&Locale::IgnoreNonSpace) result|=NORM_IGNORENONSPACE; - if(normalization&Locale::IgnoreSymbol) result|=NORM_IGNORESYMBOLS; - if(normalization&Locale::IgnoreWidth) result|=NORM_IGNOREWIDTH; - if(normalization&Locale::DigitsAsNumbers) result|=SORT_DIGITSASNUMBERS; - if(normalization&Locale::StringSoft) result|=SORT_STRINGSORT; - return result; - } - -#endif /*********************************************************************** Locale @@ -1277,587 +453,10 @@ Locale { } - Locale Locale::Invariant() - { -#if defined VCZH_MSVC - return Locale(LOCALE_NAME_INVARIANT); -#elif defined VCZH_GCC - return Locale(L""); -#endif - } - - Locale Locale::SystemDefault() - { -#if defined VCZH_MSVC - wchar_t buffer[LOCALE_NAME_MAX_LENGTH+1]={0}; - GetSystemDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH); - return Locale(buffer); -#elif defined VCZH_GCC - return Locale(L"en-US"); -#endif - } - - Locale Locale::UserDefault() - { -#if defined VCZH_MSVC - wchar_t buffer[LOCALE_NAME_MAX_LENGTH+1]={0}; - GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH); - return Locale(buffer); -#elif defined VCZH_GCC - return Locale(L"en-US"); -#endif - } - - void Locale::Enumerate(collections::List& locales) - { -#if defined VCZH_MSVC - EnumSystemLocalesEx(&Locale_EnumLocalesProcEx, LOCALE_ALL, (LPARAM)&locales, NULL); -#elif defined VCZH_GCC - locales.Add(Locale(L"en-US")); -#endif - } - const WString& Locale::GetName()const { return localeName; } - - void Locale::GetShortDateFormats(collections::List& formats)const - { -#if defined VCZH_MSVC - EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_SHORTDATE, (LPARAM)&formats); -#elif defined VCZH_GCC - formats.Add(L"MM/dd/yyyy"); - formats.Add(L"yyyy-MM-dd"); -#endif - } - - void Locale::GetLongDateFormats(collections::List& formats)const - { -#if defined VCZH_MSVC - EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_LONGDATE, (LPARAM)&formats); -#elif defined VCZH_GCC - formats.Add(L"dddd, dd MMMM yyyy"); -#endif - } - - void Locale::GetYearMonthDateFormats(collections::List& formats)const - { -#if defined VCZH_MSVC - EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_YEARMONTH, (LPARAM)&formats); -#elif defined VCZH_GCC - formats.Add(L"yyyy MMMM"); -#endif - } - - void Locale::GetLongTimeFormats(collections::List& formats)const - { -#if defined VCZH_MSVC - EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), 0, (LPARAM)&formats); -#elif defined VCZH_GCC - formats.Add(L"HH:mm:ss"); -#endif - } - - void Locale::GetShortTimeFormats(collections::List& formats)const - { -#if defined VCZH_MSVC - EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), TIME_NOSECONDS, (LPARAM)&formats); -#elif defined VCZH_GCC - formats.Add(L"HH:mm"); - formats.Add(L"hh:mm tt"); -#endif - } - - WString Locale::FormatDate(const WString& format, DateTime date)const - { -#if defined VCZH_MSVC - SYSTEMTIME st=DateTimeToSystemTime(date); - int length=GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0, NULL); - if(length==0) return L""; - Array buffer(length); - GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), &buffer[0], (int)buffer.Count(), NULL); - return &buffer[0]; -#elif defined VCZH_GCC - /* - auto df = L"yyyy,MM,MMM,MMMM,dd,ddd,dddd"; - auto ds = L"2000,01,Jan,January,02,Sun,Sunday"; - auto tf = L"hh,HH,mm,ss,tt"; - auto ts = L"01,13,02,03,PM"; - */ - WString result; - const wchar_t* reading = format.Buffer(); - - while (*reading) - { - if (wcsncmp(reading, L"yyyy", 4) == 0) - { - WString fragment = itow(date.year); - while (fragment.Length() < 4) fragment = L"0" + fragment; - result += fragment; - reading += 4; - } - else if (wcsncmp(reading, L"MMMM", 4) == 0) - { - result += GetLongMonthName(date.month); - reading += 4; - } - else if (wcsncmp(reading, L"MMM", 3) == 0) - { - result += GetShortMonthName(date.month); - reading += 3; - } - else if (wcsncmp(reading, L"MM", 2) == 0) - { - WString fragment = itow(date.month); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"dddd", 4) == 0) - { - result += GetLongDayOfWeekName(date.dayOfWeek); - reading += 4; - } - else if (wcsncmp(reading, L"ddd", 3) == 0) - { - result += GetShortDayOfWeekName(date.dayOfWeek); - reading += 3; - } - else if (wcsncmp(reading, L"dd", 2) == 0) - { - WString fragment = itow(date.day); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"hh", 2) == 0) - { - WString fragment = itow(date.hour > 12 ? date.hour - 12 : date.hour); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"HH", 2) == 0) - { - WString fragment = itow(date.hour); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"mm", 2) == 0) - { - WString fragment = itow(date.minute); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"ss", 2) == 0) - { - WString fragment = itow(date.second); - while (fragment.Length() < 2) fragment = L"0" + fragment; - result += fragment; - reading += 2; - } - else if (wcsncmp(reading, L"tt", 2) == 0) - { - result += date.hour > 12 ? L"PM" : L"AM"; - reading += 2; - } - else - { - result += WString::FromChar(*reading); - reading++; - } - } - return result; -#endif - } - - WString Locale::FormatTime(const WString& format, DateTime time)const - { -#if defined VCZH_MSVC - SYSTEMTIME st=DateTimeToSystemTime(time); - int length=GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0); - if(length==0) return L""; - Array buffer(length); - GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(),&buffer[0], (int)buffer.Count()); - return &buffer[0]; -#elif defined VCZH_GCC - return FormatDate(format, time); -#endif - } - - WString Locale::FormatNumber(const WString& number)const - { -#ifdef VCZH_MSVC - int length=GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, NULL, 0); - if(length==0) return L""; - Array buffer(length); - GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, &buffer[0], (int)buffer.Count()); - return &buffer[0]; -#elif defined VCZH_GCC - return number; -#endif - } - - WString Locale::FormatCurrency(const WString& currency)const - { -#ifdef VCZH_MSVC - int length=GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, NULL, 0); - if(length==0) return L""; - Array buffer(length); - GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, &buffer[0], (int)buffer.Count()); - return &buffer[0]; -#elif defined VCZH_GCC - return currency; -#endif - } - - WString Locale::GetShortDayOfWeekName(vint dayOfWeek)const - { -#if defined VCZH_MSVC - return FormatDate(L"ddd", DateTime::FromDateTime(2000, 1, 2+dayOfWeek)); -#elif defined VCZH_GCC - switch(dayOfWeek) - { - case 0: return L"Sun"; - case 1: return L"Mon"; - case 2: return L"Tue"; - case 3: return L"Wed"; - case 4: return L"Thu"; - case 5: return L"Fri"; - case 6: return L"Sat"; - } - return L""; -#endif - } - - WString Locale::GetLongDayOfWeekName(vint dayOfWeek)const - { -#if defined VCZH_MSVC - return FormatDate(L"dddd", DateTime::FromDateTime(2000, 1, 2+dayOfWeek)); -#elif defined VCZH_GCC - switch(dayOfWeek) - { - case 0: return L"Sunday"; - case 1: return L"Monday"; - case 2: return L"Tuesday"; - case 3: return L"Wednesday"; - case 4: return L"Thursday"; - case 5: return L"Friday"; - case 6: return L"Saturday"; - } - return L""; -#endif - } - - WString Locale::GetShortMonthName(vint month)const - { -#if defined VCZH_MSVC - return FormatDate(L"MMM", DateTime::FromDateTime(2000, month, 1)); -#elif defined VCZH_GCC - switch(month) - { - case 1: return L"Jan"; - case 2: return L"Feb"; - case 3: return L"Mar"; - case 4: return L"Apr"; - case 5: return L"May"; - case 6: return L"Jun"; - case 7: return L"Jul"; - case 8: return L"Aug"; - case 9: return L"Sep"; - case 10: return L"Oct"; - case 11: return L"Nov"; - case 12: return L"Dec"; - } - return L""; -#endif - } - - WString Locale::GetLongMonthName(vint month)const - { -#if defined VCZH_MSVC - return FormatDate(L"MMMM", DateTime::FromDateTime(2000, month, 1)); -#elif defined VCZH_GCC - switch(month) - { - case 1: return L"January"; - case 2: return L"February"; - case 3: return L"March"; - case 4: return L"April"; - case 5: return L"May"; - case 6: return L"June"; - case 7: return L"July"; - case 8: return L"August"; - case 9: return L"September"; - case 10: return L"October"; - case 11: return L"November"; - case 12: return L"December"; - } - return L""; -#endif - } - -#ifdef VCZH_MSVC - WString Locale::ToFullWidth(const WString& str)const - { - return Transform(localeName, str, LCMAP_FULLWIDTH); - } - - WString Locale::ToHalfWidth(const WString& str)const - { - return Transform(localeName, str, LCMAP_HALFWIDTH); - } - - WString Locale::ToHiragana(const WString& str)const - { - return Transform(localeName, str, LCMAP_HIRAGANA); - } - - WString Locale::ToKatagana(const WString& str)const - { - return Transform(localeName, str, LCMAP_KATAKANA); - } -#endif - - WString Locale::ToLower(const WString& str)const - { -#if defined VCZH_MSVC - return Transform(localeName, str, LCMAP_LOWERCASE); -#elif defined VCZH_GCC - return wlower(str); -#endif - } - - WString Locale::ToUpper(const WString& str)const - { -#if defined VCZH_MSVC - return Transform(localeName, str, LCMAP_UPPERCASE); -#elif defined VCZH_GCC - return wupper(str); -#endif - } - - WString Locale::ToLinguisticLower(const WString& str)const - { -#if defined VCZH_MSVC - return Transform(localeName, str, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING); -#elif defined VCZH_GCC - return wlower(str); -#endif - } - - WString Locale::ToLinguisticUpper(const WString& str)const - { -#if defined VCZH_MSVC - return Transform(localeName, str, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING); -#elif defined VCZH_GCC - return wupper(str); -#endif - } - -#ifdef VCZH_MSVC - WString Locale::ToSimplifiedChinese(const WString& str)const - { - return Transform(localeName, str, LCMAP_SIMPLIFIED_CHINESE); - } - - WString Locale::ToTraditionalChinese(const WString& str)const - { - return Transform(localeName, str, LCMAP_TRADITIONAL_CHINESE); - } - - WString Locale::ToTileCase(const WString& str)const - { - return Transform(localeName, str, LCMAP_TITLECASE); - } -#endif - - vint Locale::Compare(const WString& s1, const WString& s2, Normalization normalization)const - { -#if defined VCZH_MSVC - switch(CompareStringEx(localeName.Buffer(), TranslateNormalization(normalization), s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), NULL, NULL, NULL)) - { - case CSTR_LESS_THAN: return -1; - case CSTR_GREATER_THAN: return 1; - default: return 0; - } -#elif defined VCZH_GCC - switch(normalization) - { - case Normalization::None: - return wcscmp(s1.Buffer(), s2.Buffer()); - case Normalization::IgnoreCase: - return wcscasecmp(s1.Buffer(), s2.Buffer()); - default: - return 0; - } -#endif - } - - vint Locale::CompareOrdinal(const WString& s1, const WString& s2)const - { -#if defined VCZH_MSVC - switch(CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), FALSE)) - { - case CSTR_LESS_THAN: return -1; - case CSTR_GREATER_THAN: return 1; - default: return 0; - } -#elif defined VCZH_GCC - return wcscmp(s1.Buffer(), s2.Buffer()); -#endif - } - - vint Locale::CompareOrdinalIgnoreCase(const WString& s1, const WString& s2)const - { -#if defined VCZH_MSVC - switch(CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), TRUE)) - { - case CSTR_LESS_THAN: return -1; - case CSTR_GREATER_THAN: return 1; - default: return 0; - } -#elif defined VCZH_GCC - return wcscasecmp(s1.Buffer(), s2.Buffer()); -#endif - } - - collections::Pair Locale::FindFirst(const WString& text, const WString& find, Normalization normalization)const - { -#if defined VCZH_MSVC - int length=0; - int result=FindNLSStringEx(localeName.Buffer(), FIND_FROMSTART | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL); - return result==-1?Pair(-1, 0):Pair(result, length); -#elif defined VCZH_GCC - if(text.Length() < find.Length() || find.Length() == 0) - { - return Pair(-1, 0); - } - const wchar_t* result = 0; - switch(normalization) - { - case Normalization::None: - { - const wchar_t* reading = text.Buffer(); - while(*reading) - { - if (wcsncmp(reading, find.Buffer(), find.Length())==0) - { - result = reading; - break; - } - reading++; - } - } - break; - case Normalization::IgnoreCase: - { - const wchar_t* reading = text.Buffer(); - while(*reading) - { - if (wcsncasecmp(reading, find.Buffer(), find.Length())==0) - { - result = reading; - break; - } - reading++; - } - } - break; - } - return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); -#endif - } - - collections::Pair Locale::FindLast(const WString& text, const WString& find, Normalization normalization)const - { -#if defined VCZH_MSVC - int length=0; - int result=FindNLSStringEx(localeName.Buffer(), FIND_FROMEND | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL); - return result==-1?Pair(-1, 0):Pair(result, length); -#elif defined VCZH_GCC - if(text.Length() < find.Length() || find.Length() == 0) - { - return Pair(-1, 0); - } - const wchar_t* result = 0; - switch(normalization) - { - case Normalization::None: - { - const wchar_t* reading = text.Buffer(); - while(*reading) - { - if (wcsncmp(reading, find.Buffer(), find.Length())==0) - { - result = reading; - } - reading++; - } - } - break; - case Normalization::IgnoreCase: - { - const wchar_t* reading = text.Buffer(); - while(*reading) - { - if (wcsncasecmp(reading, find.Buffer(), find.Length())==0) - { - result = reading; - } - reading++; - } - } - break; - } - return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); -#endif - } - - bool Locale::StartsWith(const WString& text, const WString& find, Normalization normalization)const - { -#if defined VCZH_MSVC - int result=FindNLSStringEx(localeName.Buffer(), FIND_STARTSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL); - return result!=-1; -#elif defined VCZH_GCC - if(text.Length() < find.Length() || find.Length() == 0) - { - return false; - } - switch(normalization) - { - case Normalization::None: - return wcsncmp(text.Buffer(), find.Buffer(), find.Length()) == 0; - case Normalization::IgnoreCase: - return wcsncasecmp(text.Buffer(), find.Buffer(), find.Length()) == 0; - } - return false; -#endif - } - - bool Locale::EndsWith(const WString& text, const WString& find, Normalization normalization)const - { -#if defined VCZH_MSVC - int result=FindNLSStringEx(localeName.Buffer(), FIND_ENDSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL); - return result!=-1; -#elif defined VCZH_GCC - if(text.Length() < find.Length() || find.Length() == 0) - { - return false; - } - switch(normalization) - { - case Normalization::None: - return wcsncmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; - case Normalization::IgnoreCase: - return wcsncasecmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; - } - return false; -#endif - } } @@ -1869,918 +468,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#ifdef VCZH_MSVC - -namespace vl -{ - using namespace threading_internal; - using namespace collections; - -/*********************************************************************** -WaitableObject -***********************************************************************/ - - namespace threading_internal - { - struct WaitableData - { - HANDLE handle; - - WaitableData(HANDLE _handle) - :handle(_handle) - { - } - }; - } - - WaitableObject::WaitableObject() - :waitableData(0) - { - } - - void WaitableObject::SetData(threading_internal::WaitableData* data) - { - waitableData=data; - } - - bool WaitableObject::IsCreated() - { - return waitableData!=0; - } - - bool WaitableObject::Wait() - { - return WaitForTime(INFINITE); - } - - bool WaitableObject::WaitForTime(vint ms) - { - if(IsCreated()) - { - if(WaitForSingleObject(waitableData->handle, (DWORD)ms)==WAIT_OBJECT_0) - { - return true; - } - } - return false; - } - - bool WaitableObject::WaitAll(WaitableObject** objects, vint count) - { - Array handles(count); - for(vint i=0;iwaitableData->handle; - } - DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, INFINITE); - return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0; - - } - - bool WaitableObject::WaitAllForTime(WaitableObject** objects, vint count, vint ms) - { - Array handles(count); - for(vint i=0;iwaitableData->handle; - } - DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, (DWORD)ms); - return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0; - } - - vint WaitableObject::WaitAny(WaitableObject** objects, vint count, bool* abandoned) - { - Array handles(count); - for(vint i=0;iwaitableData->handle; - } - DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, INFINITE); - if(WAIT_OBJECT_0 <= result && result handles(count); - for(vint i=0;iwaitableData->handle; - } - DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, (DWORD)ms); - if(WAIT_OBJECT_0 <= result && resultdeleteAfterStopped; - ThreadLocalStorage::FixStorages(); - try - { - procedure(this, argument); - threadState=Thread::Stopped; - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - throw; - } - if(deleteAfterStopped) - { - delete this; - } - } - public: - ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped) - :procedure(_procedure) - ,argument(_argument) - ,deleteAfterStopped(_deleteAfterStopped) - { - } - }; - - class LambdaThread : public Thread - { - private: - Func procedure; - bool deleteAfterStopped; - - protected: - void Run() - { - bool deleteAfterStopped = this->deleteAfterStopped; - ThreadLocalStorage::FixStorages(); - try - { - procedure(); - threadState=Thread::Stopped; - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - throw; - } - if(deleteAfterStopped) - { - delete this; - } - } - public: - LambdaThread(const Func& _procedure, bool _deleteAfterStopped) - :procedure(_procedure) - ,deleteAfterStopped(_deleteAfterStopped) - { - } - }; - } - - void InternalThreadProc(Thread* thread) - { - thread->Run(); - } - - DWORD WINAPI InternalThreadProcWrapper(LPVOID lpParameter) - { - InternalThreadProc((Thread*)lpParameter); - return 0; - } - - Thread::Thread() - { - internalData=new ThreadData; - internalData->handle=CreateThread(NULL, 0, InternalThreadProcWrapper, this, CREATE_SUSPENDED, &internalData->id); - threadState=Thread::NotStarted; - SetData(internalData); - } - - Thread::~Thread() - { - if (internalData) - { - Stop(); - CloseHandle(internalData->handle); - delete internalData; - } - } - - Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped) - { - if(procedure) - { - Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped); - if(thread->Start()) - { - return thread; - } - else - { - delete thread; - } - } - return 0; - } - - Thread* Thread::CreateAndStart(const Func& procedure, bool deleteAfterStopped) - { - Thread* thread=new LambdaThread(procedure, deleteAfterStopped); - if(thread->Start()) - { - return thread; - } - else - { - delete thread; - } - return 0; - } - - void Thread::Sleep(vint ms) - { - ::Sleep((DWORD)ms); - } - - - vint Thread::GetCPUCount() - { - SYSTEM_INFO info; - GetSystemInfo(&info); - return info.dwNumberOfProcessors; - } - - vint Thread::GetCurrentThreadId() - { - return (vint)::GetCurrentThreadId(); - } - - bool Thread::Start() - { - if(threadState==Thread::NotStarted && internalData->handle!=NULL) - { - if(ResumeThread(internalData->handle)!=-1) - { - threadState=Thread::Running; - return true; - } - } - return false; - } - - bool Thread::Stop() - { - if(internalData->handle!=NULL) - { - if (SuspendThread(internalData->handle) != -1) - { - threadState=Thread::Stopped; - return true; - } - } - return false; - } - - Thread::ThreadState Thread::GetState() - { - return threadState; - } - - void Thread::SetCPU(vint index) - { - SetThreadAffinityMask(internalData->handle, ((vint)1 << index)); - } - -/*********************************************************************** -Mutex -***********************************************************************/ - - namespace threading_internal - { - struct MutexData : public WaitableData - { - MutexData(HANDLE _handle) - :WaitableData(_handle) - { - } - }; - } - - Mutex::Mutex() - :internalData(0) - { - } - - Mutex::~Mutex() - { - if(internalData) - { - CloseHandle(internalData->handle); - delete internalData; - } - } - - bool Mutex::Create(bool owned, const WString& name) - { - if(IsCreated())return false; - BOOL aOwned=owned?TRUE:FALSE; - LPCTSTR aName=name==L""?NULL:name.Buffer(); - HANDLE handle=CreateMutex(NULL, aOwned, aName); - if(handle) - { - internalData=new MutexData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool Mutex::Open(bool inheritable, const WString& name) - { - if(IsCreated())return false; - BOOL aInteritable=inheritable?TRUE:FALSE; - HANDLE handle=OpenMutex(SYNCHRONIZE, aInteritable, name.Buffer()); - if(handle) - { - internalData=new MutexData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool Mutex::Release() - { - if(IsCreated()) - { - return ReleaseMutex(internalData->handle)!=0; - } - return false; - } - -/*********************************************************************** -Semaphore -***********************************************************************/ - - namespace threading_internal - { - struct SemaphoreData : public WaitableData - { - SemaphoreData(HANDLE _handle) - :WaitableData(_handle) - { - } - }; - } - - Semaphore::Semaphore() - :internalData(0) - { - } - - Semaphore::~Semaphore() - { - if(internalData) - { - CloseHandle(internalData->handle); - delete internalData; - } - } - - bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name) - { - if(IsCreated())return false; - LONG aInitial=(LONG)initialCount; - LONG aMax=(LONG)maxCount; - LPCTSTR aName=name==L""?NULL:name.Buffer(); - HANDLE handle=CreateSemaphore(NULL, aInitial, aMax, aName); - if(handle) - { - internalData=new SemaphoreData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool Semaphore::Open(bool inheritable, const WString& name) - { - if(IsCreated())return false; - BOOL aInteritable=inheritable?TRUE:FALSE; - HANDLE handle=OpenSemaphore(SYNCHRONIZE, aInteritable, name.Buffer()); - if(handle) - { - internalData=new SemaphoreData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool Semaphore::Release() - { - if(IsCreated()) - { - return Release(1)!=-1; - } - return false; - } - - vint Semaphore::Release(vint count) - { - if(IsCreated()) - { - LONG previous=-1; - if(ReleaseSemaphore(internalData->handle, (LONG)count, &previous)!=0) - { - return (vint)previous; - } - } - return -1; - } - -/*********************************************************************** -EventObject -***********************************************************************/ - - namespace threading_internal - { - struct EventData : public WaitableData - { - EventData(HANDLE _handle) - :WaitableData(_handle) - { - } - }; - } - - EventObject::EventObject() - :internalData(0) - { - } - - EventObject::~EventObject() - { - if(internalData) - { - CloseHandle(internalData->handle); - delete internalData; - } - } - - bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name) - { - if(IsCreated())return false; - BOOL aSignaled=signaled?TRUE:FALSE; - LPCTSTR aName=name==L""?NULL:name.Buffer(); - HANDLE handle=CreateEvent(NULL, FALSE, aSignaled, aName); - if(handle) - { - internalData=new EventData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool EventObject::CreateManualUnsignal(bool signaled, const WString& name) - { - if(IsCreated())return false; - BOOL aSignaled=signaled?TRUE:FALSE; - LPCTSTR aName=name==L""?NULL:name.Buffer(); - HANDLE handle=CreateEvent(NULL, TRUE, aSignaled, aName); - if(handle) - { - internalData=new EventData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool EventObject::Open(bool inheritable, const WString& name) - { - if(IsCreated())return false; - BOOL aInteritable=inheritable?TRUE:FALSE; - HANDLE handle=OpenEvent(SYNCHRONIZE, aInteritable, name.Buffer()); - if(handle) - { - internalData=new EventData(handle); - SetData(internalData); - } - return IsCreated(); - } - - bool EventObject::Signal() - { - if(IsCreated()) - { - return SetEvent(internalData->handle)!=0; - } - return false; - } - - bool EventObject::Unsignal() - { - if(IsCreated()) - { - return ResetEvent(internalData->handle)!=0; - } - return false; - } - -/*********************************************************************** -ThreadPoolLite -***********************************************************************/ - - struct ThreadPoolQueueProcArgument - { - void(*proc)(void*); - void* argument; - }; - - DWORD WINAPI ThreadPoolQueueProc(void* argument) - { - Ptr proc=(ThreadPoolQueueProcArgument*)argument; - ThreadLocalStorage::FixStorages(); - try - { - proc->proc(proc->argument); - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - } - return 0; - } - - DWORD WINAPI ThreadPoolQueueFunc(void* argument) - { - Ptr> proc=(Func*)argument; - ThreadLocalStorage::FixStorages(); - try - { - (*proc.Obj())(); - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - } - return 0; - } - - ThreadPoolLite::ThreadPoolLite() - { - } - - ThreadPoolLite::~ThreadPoolLite() - { - } - - bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument) - { - ThreadPoolQueueProcArgument* p=new ThreadPoolQueueProcArgument; - p->proc=proc; - p->argument=argument; - if(QueueUserWorkItem(&ThreadPoolQueueProc, p, WT_EXECUTEDEFAULT)) - { - return true; - } - else - { - delete p; - return false; - } - } - - bool ThreadPoolLite::Queue(const Func& proc) - { - Func* p=new Func(proc); - if(QueueUserWorkItem(&ThreadPoolQueueFunc, p, WT_EXECUTEDEFAULT)) - { - return true; - } - else - { - delete p; - return false; - } - } - -/*********************************************************************** -CriticalSection -***********************************************************************/ - - namespace threading_internal - { - struct CriticalSectionData - { - CRITICAL_SECTION criticalSection; - }; - } - - CriticalSection::Scope::Scope(CriticalSection& _criticalSection) - :criticalSection(&_criticalSection) - { - criticalSection->Enter(); - } - - CriticalSection::Scope::~Scope() - { - criticalSection->Leave(); - } - - CriticalSection::CriticalSection() - { - internalData=new CriticalSectionData; - InitializeCriticalSection(&internalData->criticalSection); - } - - CriticalSection::~CriticalSection() - { - DeleteCriticalSection(&internalData->criticalSection); - delete internalData; - } - - bool CriticalSection::TryEnter() - { - return TryEnterCriticalSection(&internalData->criticalSection)!=0; - } - - void CriticalSection::Enter() - { - EnterCriticalSection(&internalData->criticalSection); - } - - void CriticalSection::Leave() - { - LeaveCriticalSection(&internalData->criticalSection); - } - -/*********************************************************************** -ReaderWriterLock -***********************************************************************/ - - namespace threading_internal - { - struct ReaderWriterLockData - { - SRWLOCK lock; - }; - } - - ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock) - :lock(&_lock) - { - lock->EnterReader(); - } - - ReaderWriterLock::ReaderScope::~ReaderScope() - { - lock->LeaveReader(); - } - - ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock) - :lock(&_lock) - { - lock->EnterWriter(); - } - - ReaderWriterLock::WriterScope::~WriterScope() - { - lock->LeaveWriter(); - } - - ReaderWriterLock::ReaderWriterLock() - :internalData(new threading_internal::ReaderWriterLockData) - { - InitializeSRWLock(&internalData->lock); - } - - ReaderWriterLock::~ReaderWriterLock() - { - delete internalData; - } - - bool ReaderWriterLock::TryEnterReader() - { - return TryAcquireSRWLockShared(&internalData->lock)!=0; - } - - void ReaderWriterLock::EnterReader() - { - AcquireSRWLockShared(&internalData->lock); - } - - void ReaderWriterLock::LeaveReader() - { - ReleaseSRWLockShared(&internalData->lock); - } - - bool ReaderWriterLock::TryEnterWriter() - { - return TryAcquireSRWLockExclusive(&internalData->lock)!=0; - } - - void ReaderWriterLock::EnterWriter() - { - AcquireSRWLockExclusive(&internalData->lock); - } - - void ReaderWriterLock::LeaveWriter() - { - ReleaseSRWLockExclusive(&internalData->lock); - } - -/*********************************************************************** -ConditionVariable -***********************************************************************/ - - namespace threading_internal - { - struct ConditionVariableData - { - CONDITION_VARIABLE variable; - }; - } - - ConditionVariable::ConditionVariable() - :internalData(new threading_internal::ConditionVariableData) - { - InitializeConditionVariable(&internalData->variable); - } - - ConditionVariable::~ConditionVariable() - { - delete internalData; - } - - bool ConditionVariable::SleepWith(CriticalSection& cs) - { - return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, INFINITE)!=0; - } - - bool ConditionVariable::SleepWithForTime(CriticalSection& cs, vint ms) - { - return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, (DWORD)ms)!=0; - } - - bool ConditionVariable::SleepWithReader(ReaderWriterLock& lock) - { - return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0; - } - - bool ConditionVariable::SleepWithReaderForTime(ReaderWriterLock& lock, vint ms) - { - return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0; - } - - bool ConditionVariable::SleepWithWriter(ReaderWriterLock& lock) - { - return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, 0)!=0; - } - - bool ConditionVariable::SleepWithWriterForTime(ReaderWriterLock& lock, vint ms) - { - return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, 0)!=0; - } - - void ConditionVariable::WakeOnePending() - { - WakeConditionVariable(&internalData->variable); - } - - void ConditionVariable::WakeAllPendings() - { - WakeAllConditionVariable(&internalData->variable); - } - -/*********************************************************************** -SpinLock -***********************************************************************/ - - SpinLock::Scope::Scope(SpinLock& _spinLock) - :spinLock(&_spinLock) - { - spinLock->Enter(); - } - - SpinLock::Scope::~Scope() - { - spinLock->Leave(); - } - - SpinLock::SpinLock() - :token(0) - { - } - - SpinLock::~SpinLock() - { - } - - bool SpinLock::TryEnter() - { - return _InterlockedExchange(&token, 1)==0; - } - - void SpinLock::Enter() - { - while(_InterlockedCompareExchange(&token, 1, 0)!=0) - { - while(token!=0) _mm_pause(); - } - } - - void SpinLock::Leave() - { - _InterlockedExchange(&token, 0); - } - -/*********************************************************************** -ThreadLocalStorage -***********************************************************************/ - -#define KEY ((DWORD&)key) - - ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor) - :destructor(_destructor) - { - static_assert(sizeof(key) >= sizeof(DWORD), "ThreadLocalStorage's key storage is not large enouth."); - PushStorage(this); - KEY = TlsAlloc(); - CHECK_ERROR(KEY != TLS_OUT_OF_INDEXES, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to alloc new thread local storage index."); - } - - ThreadLocalStorage::~ThreadLocalStorage() - { - TlsFree(KEY); - } - - void* ThreadLocalStorage::Get() - { - CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage."); - return TlsGetValue(KEY); - } - - void ThreadLocalStorage::Set(void* data) - { - CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage."); - TlsSetValue(KEY, data); - } - -#undef KEY -} -#endif - -/*********************************************************************** -ThreadLocalStorage Common Implementations -***********************************************************************/ namespace vl { @@ -2857,928 +544,6 @@ namespace vl } -/*********************************************************************** -.\THREADINGLINUX.CPP -***********************************************************************/ -/*********************************************************************** -Author: Zihan Chen (vczh) -Licensed under https://github.com/vczh-libraries/License -***********************************************************************/ - -#ifdef VCZH_GCC -#include -#include -#include -#include -#if defined(__APPLE__) || defined(__APPLE_CC__) -#include -#endif - -namespace vl -{ - using namespace threading_internal; - using namespace collections; - - -/*********************************************************************** -Thread -***********************************************************************/ - - namespace threading_internal - { - struct ThreadData - { - pthread_t id; - EventObject ev; - }; - - class ProceduredThread : public Thread - { - private: - Thread::ThreadProcedure procedure; - void* argument; - bool deleteAfterStopped; - - protected: - void Run() - { - bool deleteAfterStopped = this->deleteAfterStopped; - ThreadLocalStorage::FixStorages(); - try - { - procedure(this, argument); - threadState=Thread::Stopped; - internalData->ev.Signal(); - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - throw; - } - if(deleteAfterStopped) - { - delete this; - } - } - public: - ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped) - :procedure(_procedure) - ,argument(_argument) - ,deleteAfterStopped(_deleteAfterStopped) - { - } - }; - - class LambdaThread : public Thread - { - private: - Func procedure; - bool deleteAfterStopped; - - protected: - void Run() - { - bool deleteAfterStopped = this->deleteAfterStopped; - ThreadLocalStorage::FixStorages(); - try - { - procedure(); - threadState=Thread::Stopped; - internalData->ev.Signal(); - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - throw; - } - if(deleteAfterStopped) - { - delete this; - } - } - public: - LambdaThread(const Func& _procedure, bool _deleteAfterStopped) - :procedure(_procedure) - ,deleteAfterStopped(_deleteAfterStopped) - { - } - }; - } - - void InternalThreadProc(Thread* thread) - { - thread->Run(); - } - - void* InternalThreadProcWrapper(void* lpParameter) - { - InternalThreadProc((Thread*)lpParameter); - return 0; - } - - Thread::Thread() - { - internalData=new ThreadData; - internalData->ev.CreateManualUnsignal(false); - threadState=Thread::NotStarted; - } - - Thread::~Thread() - { - if (internalData) - { - Stop(); - if (threadState!=Thread::NotStarted) - { - pthread_detach(internalData->id); - } - delete internalData; - } - } - - Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped) - { - if(procedure) - { - Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped); - if(thread->Start()) - { - return thread; - } - else - { - delete thread; - } - } - return 0; - } - - Thread* Thread::CreateAndStart(const Func& procedure, bool deleteAfterStopped) - { - Thread* thread=new LambdaThread(procedure, deleteAfterStopped); - if(thread->Start()) - { - return thread; - } - else - { - delete thread; - } - return 0; - } - - void Thread::Sleep(vint ms) - { - if (ms >= 1000) - { - sleep(ms / 1000); - } - if (ms % 1000) - { - usleep((ms % 1000) * 1000); - } - } - - vint Thread::GetCPUCount() - { - return (vint)sysconf(_SC_NPROCESSORS_ONLN); - } - - vint Thread::GetCurrentThreadId() - { - return (vint)::pthread_self(); - } - - bool Thread::Start() - { - if(threadState==Thread::NotStarted) - { - if(pthread_create(&internalData->id, nullptr, &InternalThreadProcWrapper, this)==0) - { - threadState=Thread::Running; - return true; - } - } - return false; - } - - bool Thread::Wait() - { - return internalData->ev.Wait(); - } - - bool Thread::Stop() - { - if (threadState==Thread::Running) - { - if(pthread_cancel(internalData->id)==0) - { - threadState=Thread::Stopped; - internalData->ev.Signal(); - return true; - } - } - return false; - } - - Thread::ThreadState Thread::GetState() - { - return threadState; - } - -/*********************************************************************** -Mutex -***********************************************************************/ - - namespace threading_internal - { - struct MutexData - { - Semaphore sem; - }; - }; - - Mutex::Mutex() - { - internalData = new MutexData; - } - - Mutex::~Mutex() - { - delete internalData; - } - - bool Mutex::Create(bool owned, const WString& name) - { - return internalData->sem.Create(owned ? 0 : 1, 1, name); - } - - bool Mutex::Open(bool inheritable, const WString& name) - { - return internalData->sem.Open(inheritable, name); - } - - bool Mutex::Release() - { - return internalData->sem.Release(); - } - - bool Mutex::Wait() - { - return internalData->sem.Wait(); - } - -/*********************************************************************** -Semaphore -***********************************************************************/ - - namespace threading_internal - { - struct SemaphoreData - { - sem_t semUnnamed; - sem_t* semNamed = nullptr; - }; - } - - Semaphore::Semaphore() - :internalData(0) - { - } - - Semaphore::~Semaphore() - { - if (internalData) - { - if (internalData->semNamed) - { - sem_close(internalData->semNamed); - } - else - { - sem_destroy(&internalData->semUnnamed); - } - delete internalData; - } - } - - bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name) - { - if (internalData) return false; - if (initialCount > maxCount) return false; - - internalData = new SemaphoreData; -#if defined(__APPLE__) - - AString auuid; - if(name.Length() == 0) - { - CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault); - CFStringRef cfstr = CFUUIDCreateString(kCFAllocatorDefault, cfuuid); - auuid = CFStringGetCStringPtr(cfstr, kCFStringEncodingASCII); - - CFRelease(cfstr); - CFRelease(cfuuid); - } - auuid = auuid.Insert(0, "/"); - // OSX SEM_NAME_LENGTH = 31 - if(auuid.Length() >= 30) - auuid = auuid.Sub(0, 30); - - if ((internalData->semNamed = sem_open(auuid.Buffer(), O_CREAT, O_RDWR, initialCount)) == SEM_FAILED) - { - delete internalData; - internalData = 0; - return false; - } - -#else - if (name == L"") - { - if(sem_init(&internalData->semUnnamed, 0, (int)initialCount) == -1) - { - delete internalData; - internalData = 0; - return false; - } - } - else - { - AString astr = wtoa(name); - - if ((internalData->semNamed = sem_open(astr.Buffer(), O_CREAT, 0777, initialCount)) == SEM_FAILED) - { - delete internalData; - internalData = 0; - return false; - } - } -#endif - - Release(initialCount); - return true; - } - - bool Semaphore::Open(bool inheritable, const WString& name) - { - if (internalData) return false; - if (inheritable) return false; - - internalData = new SemaphoreData; - if (!(internalData->semNamed = sem_open(wtoa(name).Buffer(), 0))) - { - delete internalData; - internalData = 0; - return false; - } - - return true; - } - - bool Semaphore::Release() - { - return Release(1); - } - - vint Semaphore::Release(vint count) - { - for (vint i = 0; i < count; i++) - { - if (internalData->semNamed) - { - sem_post(internalData->semNamed); - } - else - { - sem_post(&internalData->semUnnamed); - } - } - return true; - } - - bool Semaphore::Wait() - { - if (internalData->semNamed) - { - return sem_wait(internalData->semNamed) == 0; - } - else - { - return sem_wait(&internalData->semUnnamed) == 0; - } - } - -/*********************************************************************** -EventObject -***********************************************************************/ - - namespace threading_internal - { - struct EventData - { - bool autoReset; - volatile bool signaled; - CriticalSection mutex; - ConditionVariable cond; - volatile vint counter = 0; - }; - } - - EventObject::EventObject() - { - internalData = nullptr; - } - - EventObject::~EventObject() - { - if (internalData) - { - delete internalData; - } - } - - bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name) - { - if (name!=L"") return false; - if (internalData) return false; - - internalData = new EventData; - internalData->autoReset = true; - internalData->signaled = signaled; - return true; - } - - bool EventObject::CreateManualUnsignal(bool signaled, const WString& name) - { - if (name!=L"") return false; - if (internalData) return false; - - internalData = new EventData; - internalData->autoReset = false; - internalData->signaled = signaled; - return true; - } - - bool EventObject::Signal() - { - if (!internalData) return false; - - internalData->mutex.Enter(); - internalData->signaled = true; - if (internalData->counter) - { - if (internalData->autoReset) - { - internalData->cond.WakeOnePending(); - internalData->signaled = false; - } - else - { - internalData->cond.WakeAllPendings(); - } - } - internalData->mutex.Leave(); - return true; - } - - bool EventObject::Unsignal() - { - if (!internalData) return false; - - internalData->mutex.Enter(); - internalData->signaled = false; - internalData->mutex.Leave(); - return true; - } - - bool EventObject::Wait() - { - if (!internalData) return false; - - internalData->mutex.Enter(); - if (internalData->signaled) - { - if (internalData->autoReset) - { - internalData->signaled = false; - } - } - else - { - INCRC(&internalData->counter); - internalData->cond.SleepWith(internalData->mutex); - DECRC(&internalData->counter); - } - internalData->mutex.Leave(); - return true; - } - -/*********************************************************************** -ThreadPoolLite -***********************************************************************/ - - namespace threading_internal - { - struct ThreadPoolTask - { - Func task; - Ptr next; - }; - - struct ThreadPoolData - { - Semaphore semaphore; - EventObject taskFinishEvent; - Ptr taskBegin; - Ptr* taskEnd = nullptr; - volatile bool stopping = false; - List taskThreads; - }; - - SpinLock threadPoolLock; - ThreadPoolData* threadPoolData = nullptr; - - void ThreadPoolProc(Thread* thread, void* argument) - { - while (true) - { - Ptr task; - - threadPoolData->semaphore.Wait(); - SPIN_LOCK(threadPoolLock) - { - if (threadPoolData->taskBegin) - { - task = threadPoolData->taskBegin; - threadPoolData->taskBegin = task->next; - } - - if (!threadPoolData->taskBegin) - { - threadPoolData->taskEnd = &threadPoolData->taskBegin; - threadPoolData->taskFinishEvent.Signal(); - } - } - - if (task) - { - ThreadLocalStorage::FixStorages(); - try - { - task->task(); - ThreadLocalStorage::ClearStorages(); - } - catch (...) - { - ThreadLocalStorage::ClearStorages(); - } - } - else if (threadPoolData->stopping) - { - return; - } - } - } - - bool ThreadPoolQueue(const Func& proc) - { - SPIN_LOCK(threadPoolLock) - { - if (!threadPoolData) - { - threadPoolData = new ThreadPoolData; - threadPoolData->semaphore.Create(0, 65536); - threadPoolData->taskFinishEvent.CreateManualUnsignal(false); - threadPoolData->taskEnd = &threadPoolData->taskBegin; - - for (vint i = 0; i < Thread::GetCPUCount() * 4; i++) - { - threadPoolData->taskThreads.Add(Thread::CreateAndStart(&ThreadPoolProc, nullptr, false)); - } - } - - if (threadPoolData) - { - if (threadPoolData->stopping) - { - return false; - } - - auto task = MakePtr(); - task->task = proc; - *threadPoolData->taskEnd = task; - threadPoolData->taskEnd = &task->next; - threadPoolData->semaphore.Release(); - threadPoolData->taskFinishEvent.Unsignal(); - } - } - return true; - } - - bool ThreadPoolStop(bool discardPendingTasks) - { - SPIN_LOCK(threadPoolLock) - { - if (!threadPoolData) return false; - if (threadPoolData->stopping) return false; - - threadPoolData->stopping = true; - if (discardPendingTasks) - { - threadPoolData->taskEnd = &threadPoolData->taskBegin; - threadPoolData->taskBegin = nullptr; - } - - threadPoolData->semaphore.Release(threadPoolData->taskThreads.Count()); - } - - threadPoolData->taskFinishEvent.Wait(); - for (vint i = 0; i < threadPoolData->taskThreads.Count(); i++) - { - auto thread = threadPoolData->taskThreads[i]; - thread->Wait(); - delete thread; - } - threadPoolData->taskThreads.Clear(); - - SPIN_LOCK(threadPoolLock) - { - delete threadPoolData; - threadPoolData = nullptr; - } - return true; - } - } - - ThreadPoolLite::ThreadPoolLite() - { - } - - ThreadPoolLite::~ThreadPoolLite() - { - } - - bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument) - { - return ThreadPoolQueue([proc, argument](){proc(argument);}); - } - - bool ThreadPoolLite::Queue(const Func& proc) - { - return ThreadPoolQueue(proc); - } - - bool ThreadPoolLite::Stop(bool discardPendingTasks) - { - return ThreadPoolStop(discardPendingTasks); - } - -/*********************************************************************** -CriticalSection -***********************************************************************/ - - namespace threading_internal - { - struct CriticalSectionData - { - pthread_mutex_t mutex; - }; - } - - CriticalSection::CriticalSection() - { - internalData = new CriticalSectionData; - pthread_mutex_init(&internalData->mutex, nullptr); - } - - CriticalSection::~CriticalSection() - { - pthread_mutex_destroy(&internalData->mutex); - delete internalData; - } - - bool CriticalSection::TryEnter() - { - return pthread_mutex_trylock(&internalData->mutex) == 0; - } - - void CriticalSection::Enter() - { - pthread_mutex_lock(&internalData->mutex); - } - - void CriticalSection::Leave() - { - pthread_mutex_unlock(&internalData->mutex); - } - - CriticalSection::Scope::Scope(CriticalSection& _criticalSection) - :criticalSection(&_criticalSection) - { - criticalSection->Enter(); - } - - CriticalSection::Scope::~Scope() - { - criticalSection->Leave(); - } - -/*********************************************************************** -ReaderWriterLock -***********************************************************************/ - - namespace threading_internal - { - struct ReaderWriterLockData - { - pthread_rwlock_t rwlock; - }; - } - - ReaderWriterLock::ReaderWriterLock() - { - internalData = new ReaderWriterLockData; - pthread_rwlock_init(&internalData->rwlock, nullptr); - } - - ReaderWriterLock::~ReaderWriterLock() - { - pthread_rwlock_destroy(&internalData->rwlock); - delete internalData; - } - - bool ReaderWriterLock::TryEnterReader() - { - return pthread_rwlock_tryrdlock(&internalData->rwlock) == 0; - } - - void ReaderWriterLock::EnterReader() - { - pthread_rwlock_rdlock(&internalData->rwlock); - } - - void ReaderWriterLock::LeaveReader() - { - pthread_rwlock_unlock(&internalData->rwlock); - } - - bool ReaderWriterLock::TryEnterWriter() - { - return pthread_rwlock_trywrlock(&internalData->rwlock) == 0; - } - - void ReaderWriterLock::EnterWriter() - { - pthread_rwlock_wrlock(&internalData->rwlock); - } - - void ReaderWriterLock::LeaveWriter() - { - pthread_rwlock_unlock(&internalData->rwlock); - } - - ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock) - :lock(&_lock) - { - lock->EnterReader(); - } - - ReaderWriterLock::ReaderScope::~ReaderScope() - { - lock->LeaveReader(); - } - - ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock) - :lock(&_lock) - { - lock->EnterWriter(); - } - - ReaderWriterLock::WriterScope::~WriterScope() - { - lock->LeaveReader(); - } - -/*********************************************************************** -ConditionVariable -***********************************************************************/ - - namespace threading_internal - { - struct ConditionVariableData - { - pthread_cond_t cond; - }; - } - - ConditionVariable::ConditionVariable() - { - internalData = new ConditionVariableData; - pthread_cond_init(&internalData->cond, nullptr); - } - - ConditionVariable::~ConditionVariable() - { - pthread_cond_destroy(&internalData->cond); - delete internalData; - } - - bool ConditionVariable::SleepWith(CriticalSection& cs) - { - return pthread_cond_wait(&internalData->cond, &cs.internalData->mutex) == 0; - } - - void ConditionVariable::WakeOnePending() - { - pthread_cond_signal(&internalData->cond); - } - - void ConditionVariable::WakeAllPendings() - { - pthread_cond_broadcast(&internalData->cond); - } - -/*********************************************************************** -SpinLock -***********************************************************************/ - - SpinLock::Scope::Scope(SpinLock& _spinLock) - :spinLock(&_spinLock) - { - spinLock->Enter(); - } - - SpinLock::Scope::~Scope() - { - spinLock->Leave(); - } - - SpinLock::SpinLock() - :token(0) - { - } - - SpinLock::~SpinLock() - { - } - - bool SpinLock::TryEnter() - { - return __sync_lock_test_and_set(&token, 1)==0; - } - - void SpinLock::Enter() - { - while(__sync_val_compare_and_swap(&token, 0, 1)!=0) - { - while(token!=0) _mm_pause(); - } - } - - void SpinLock::Leave() - { - __sync_lock_test_and_set(&token, 0); - } - -/*********************************************************************** -ThreadLocalStorage -***********************************************************************/ - -#define KEY ((pthread_key_t&)key) - - ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor) - :destructor(_destructor) - { - static_assert(sizeof(key) >= sizeof(pthread_key_t), "ThreadLocalStorage's key storage is not large enouth."); - PushStorage(this); - auto error = pthread_key_create(&KEY, destructor); - CHECK_ERROR(error != EAGAIN && error != ENOMEM, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to create a thread local storage index."); - } - - ThreadLocalStorage::~ThreadLocalStorage() - { - pthread_key_delete(KEY); - } - - void* ThreadLocalStorage::Get() - { - CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage."); - return pthread_getspecific(KEY); - } - - void ThreadLocalStorage::Set(void* data) - { - CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage."); - pthread_setspecific(KEY, data); - } - -#undef KEY -} -#endif - - /*********************************************************************** .\STREAM\ACCESSOR.CPP ***********************************************************************/ @@ -4608,10 +1373,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#include -#elif defined VCZH_GCC -#endif namespace vl { @@ -4753,27 +1514,8 @@ CharDecoder Mbcs ***********************************************************************/ - vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) - { -#if defined VCZH_MSVC - vint length = WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL); - char* mbcs = new char[length]; - WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL); - vint result = stream->Write(mbcs, length); - delete[] mbcs; -#elif defined VCZH_GCC - WString w = WString::CopyFrom(_buffer, chars); - AString a = wtoa(w); - vint length = a.Length(); - vint result = stream->Write((void*)a.Buffer(), length); -#endif - if (result != length) - { - Close(); - return 0; - } - return chars; - } + extern bool IsMbcsLeadByte(char c); + extern void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars); vint MbcsDecoder::ReadString(wchar_t* _buffer, vint chars) { @@ -4786,11 +1528,7 @@ Mbcs { break; } -#if defined VCZH_MSVC - if (IsDBCSLeadByte(*reading)) -#elif defined VCZH_GCC - if ((vint8_t)*reading < 0) -#endif + if (IsMbcsLeadByte(*reading)) { if (stream->Read(reading + 1, 1) != 1) { @@ -4804,13 +1542,8 @@ Mbcs } readed++; } -#if defined VCZH_MSVC - MultiByteToWideChar(CP_THREAD_ACP, 0, source, (int)(reading - source), _buffer, (int)chars); -#elif defined VCZH_GCC - AString a = AString::CopyFrom(source, (vint)(reading - source)); - WString w = atow(a); - memcpy(_buffer, w.Buffer(), readed * sizeof(wchar_t)); -#endif + + MbcsToWChar(_buffer, chars, readed, source, (vint)(reading - source)); delete[] source; return readed; } @@ -4954,40 +1687,6 @@ Utf-16-be Utf8 ***********************************************************************/ - vint Utf8Encoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) - { -#if defined VCZH_MSVC - vint length = WideCharToMultiByte(CP_UTF8, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL); - char* mbcs = new char[length]; - WideCharToMultiByte(CP_UTF8, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL); - vint result = stream->Write(mbcs, length); - delete[] mbcs; - if (result != length) - { - Close(); - return 0; - } - return chars; -#elif defined VCZH_GCC - WCharToUtfReader reader(_buffer, chars); - while (char8_t c = reader.Read()) - { - vint written = stream->Write(&c, sizeof(c)); - if (written != sizeof(c)) - { - Close(); - return 0; - } - } - if (reader.HasIllegalChar()) - { - Close(); - return 0; - } - return chars; -#endif - } - vint Utf8Decoder::ReadString(wchar_t* _buffer, vint chars) { reader.Setup(stream); @@ -5305,9 +2004,6 @@ Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ -#if defined VCZH_MSVC -#elif defined VCZH_GCC -#endif namespace vl { @@ -5440,26 +2136,24 @@ Helper Functions } return !needTrail; } - -#if defined VCZH_MSVC - template - bool GetEncodingResult(int(&tests)[Count], bool(&results)[Count], int test) - { - for (vint i = 0; i < Count; i++) - { - if (tests[i] & test) - { - if (results[i]) return true; - } - } - return false; - } -#endif /*********************************************************************** TestEncoding ***********************************************************************/ + extern void TestEncodingInternal( + unsigned char* buffer, + vint size, + BomEncoder::Encoding& encoding, + bool containsBom, + bool utf16HitSurrogatePairs, + bool utf16BEHitSurrogatePairs, + bool roughMbcs, + bool roughUtf8, + bool roughUtf16, + bool roughUtf16BE + ); + void TestEncoding(unsigned char* buffer, vint size, BomEncoder::Encoding& encoding, bool& containsBom) { if (size >= 3 && strncmp((char*)buffer, "\xEF\xBB\xBF", 3) == 0) @@ -5498,122 +2192,7 @@ TestEncoding } else if (roughCount > 1) { -#if defined VCZH_MSVC - int tests[] = - { - IS_TEXT_UNICODE_REVERSE_ASCII16, - IS_TEXT_UNICODE_REVERSE_STATISTICS, - IS_TEXT_UNICODE_REVERSE_CONTROLS, - - IS_TEXT_UNICODE_ASCII16, - IS_TEXT_UNICODE_STATISTICS, - IS_TEXT_UNICODE_CONTROLS, - - IS_TEXT_UNICODE_ILLEGAL_CHARS, - IS_TEXT_UNICODE_ODD_LENGTH, - IS_TEXT_UNICODE_NULL_BYTES, - }; - - const vint TestCount = sizeof(tests) / sizeof(*tests); - bool results[TestCount]; - for (vint i = 0; i < TestCount; i++) - { - int test = tests[i]; - results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; - } - - if (size % 2 == 0 - && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16) - && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_STATISTICS) - && !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS) - ) - { - for (vint i = 0; i < size; i += 2) - { - unsigned char c = buffer[i]; - buffer[i] = buffer[i + 1]; - buffer[i + 1] = c; - } - // 3 = (count of reverse group) = (count of unicode group) - for (vint i = 0; i < 3; i++) - { - int test = tests[i + 3]; - results[i] = IsTextUnicode(buffer, (int)size, &test) != 0; - } - for (vint i = 0; i < size; i += 2) - { - unsigned char c = buffer[i]; - buffer[i] = buffer[i + 1]; - buffer[i + 1] = c; - } - } - - if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) - { - if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_ASCII_MASK)) - { - encoding = BomEncoder::Utf8; - } - else if (roughUtf8 || !roughMbcs) - { - encoding = BomEncoder::Utf8; - } - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_ASCII16)) - { - encoding = BomEncoder::Utf16; - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16)) - { - encoding = BomEncoder::Utf16BE; - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_CONTROLS)) - { - encoding = BomEncoder::Utf16; - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS)) - { - encoding = BomEncoder::Utf16BE; - } - else - { - if (!roughUtf8) - { - if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) - { - encoding = BomEncoder::Utf16; - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS)) - { - encoding = BomEncoder::Utf16BE; - } - } - else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK)) - { - encoding = BomEncoder::Utf8; - } - else if (roughUtf8 || !roughMbcs) - { - encoding = BomEncoder::Utf8; - } - } -#elif defined VCZH_GCC - if (roughUtf16 && roughUtf16BE && !roughUtf8) - { - if (utf16BEHitSurrogatePairs && !utf16HitSurrogatePairs) - { - encoding = BomEncoder::Utf16BE; - } - else - { - encoding = BomEncoder::Utf16; - } - } - else - { - encoding = BomEncoder::Utf8; - } -#endif + TestEncodingInternal(buffer, size, encoding, containsBom, utf16HitSurrogatePairs, utf16BEHitSurrogatePairs, roughMbcs, roughUtf8, roughUtf16, roughUtf16BE); } } } diff --git a/Import/VlppOS.h b/Import/VlppOS.h index 3c7f74f7..b9b4759e 100644 --- a/Import/VlppOS.h +++ b/Import/VlppOS.h @@ -1885,6 +1885,8 @@ namespace vl protected: WString fullPath; + static void NormalizeDelimiters(collections::Array& buffer); + static void TrimLastDelimiter(WString& fullPath); void Initialize(); static void GetPathComponents(WString path, collections::List& components); @@ -1904,7 +1906,7 @@ namespace vl /// Create a root path. /// returns different values for root path on different platforms. Do not rely on the value. - FilePath(); + FilePath() = default; /// Create a file path. /// Content of the file path. If it is a relative path, it will be converted to an absolute path. FilePath(const WString& _filePath); @@ -1914,7 +1916,7 @@ namespace vl /// Copy a file path. /// The file path to copy. FilePath(const FilePath& _filePath); - ~FilePath(); + ~FilePath() = default; static vint Compare(const FilePath& a, const FilePath& b); bool operator==(const FilePath& filePath)const{ return Compare(*this, filePath) == 0; } @@ -1964,11 +1966,11 @@ namespace vl public: /// Create an empty reference. An empty reference does not refer to any file. - File(); + File() = default; /// Create a reference to a specified file. The file is not required to exist. /// The specified file. File(const FilePath& _filePath); - ~File(); + ~File() = default; /// Get the file path of the file. /// The file path. @@ -2031,13 +2033,15 @@ namespace vl private: FilePath filePath; + bool CreateNonRecursively()const; + bool DeleteNonRecursively()const; public: /// Create a reference to the root folder. - Folder(); + Folder() = default; /// Create a reference to a specified folder. The folder is not required to exist. /// The specified folder. Folder(const FilePath& _filePath); - ~Folder(); + ~Folder() = default; /// Get the file path of the folder. /// The file path.