/*********************************************************************** 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 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 // TODO: (enumerable) Linq:Select for (vint i = 0; i < request.acceptTypes.Count(); i++) { acceptTypes.Add(request.acceptTypes.Get(i).Buffer()); } acceptTypes.Add(nullptr); 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 ***********************************************************************/ DWORD WINAPI ThreadPoolQueueFunc(void* argument) { auto proc=Ptr((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) { return Queue([=]() {proc(argument); }); } 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); } /*********************************************************************** 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; } } } } }