/*********************************************************************** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY DEVELOPER: Zihan Chen(vczh) ***********************************************************************/ #include "VlppOS.h" /*********************************************************************** .\FILESYSTEM.CPP ***********************************************************************/ /*********************************************************************** 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 { namespace filesystem { using namespace collections; using namespace stream; // ReadDirectoryChangesW /*********************************************************************** FilePath ***********************************************************************/ #if defined VCZH_GCC const wchar_t FilePath::Delimiter; #endif void FilePath::Initialize() { { 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'/') { 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; } } #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)) + 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 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) { Initialize(); } FilePath::FilePath(const wchar_t* _filePath) :fullPath(_filePath) { Initialize(); } FilePath::FilePath(const FilePath& _filePath) :fullPath(_filePath.fullPath) { } FilePath::~FilePath() { } vint FilePath::Compare(const FilePath& a, const FilePath& b) { return WString::Compare(a.fullPath, b.fullPath); } FilePath FilePath::operator/(const WString& relativePath)const { if (IsRoot()) { return relativePath; } else { return fullPath + L"/" + relativePath; } } 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 { WString delimiter = Delimiter; auto index = INVLOC.FindLast(fullPath, delimiter, Locale::None); if (index.key == -1) return fullPath; return fullPath.Right(fullPath.Length() - index.key - 1); } FilePath FilePath::GetFolder()const { WString delimiter = Delimiter; auto index = INVLOC.FindLast(fullPath, delimiter, Locale::None); if (index.key == -1) return FilePath(); return fullPath.Left(index.key); } WString FilePath::GetFullPath()const { 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; WString delimiter = Delimiter; components.Clear(); while(true) { auto index = INVLOC.FindFirst(pathRemaining, delimiter, Locale::None); if (index.key == -1) break; 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) { // Windows UNC Path starting with "\\" // components[0] will be L"\\" components.Add(L"\\"); index.value++; } #endif } pathRemaining = pathRemaining.Right(pathRemaining.Length() - (index.key + index.value)); } if(pathRemaining.Length() != 0) { components.Add(pathRemaining); } } WString FilePath::ComponentsToPath(const collections::List& components) { WString result; WString delimiter = 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) { 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"\\") { result += delimiter; i++; } #endif for(; i < components.Count(); i++) { result += components[i]; if(i + 1 < components.Count()) result += delimiter; } return result; } /*********************************************************************** File ***********************************************************************/ File::File() { } File::File(const FilePath& _filePath) :filePath(_filePath) { } File::~File() { } const FilePath& File::GetFilePath()const { return filePath; } bool File::ReadAllTextWithEncodingTesting(WString& text, stream::BomEncoder::Encoding& encoding, bool& containsBom) { Array buffer; { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; if (fileStream.Size() == 0) { text = L""; encoding = BomEncoder::Mbcs; containsBom = false; return true; } buffer.Resize((vint)fileStream.Size()); vint count = fileStream.Read(&buffer[0], buffer.Count()); CHECK_ERROR(count == buffer.Count(), L"vl::filesystem::File::ReadAllTextWithEncodingTesting(WString&, BomEncoder::Encoding&, bool&)#Failed to read the whole file."); } TestEncoding(&buffer[0], buffer.Count(), encoding, containsBom); MemoryWrapperStream memoryStream(&buffer[0], buffer.Count()); if (containsBom) { BomDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } else { switch (encoding) { case BomEncoder::Utf8: { Utf8Decoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; case BomEncoder::Utf16: { Utf16Decoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; case BomEncoder::Utf16BE: { Utf16BEDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; default: { MbcsDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } } } return true; } WString File::ReadAllTextByBom()const { WString text; ReadAllTextByBom(text); return text; } bool File::ReadAllTextByBom(WString& text)const { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; BomDecoder decoder; DecoderStream decoderStream(fileStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); return true; } bool File::ReadAllLinesByBom(collections::List& lines)const { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; BomDecoder decoder; DecoderStream decoderStream(fileStream, decoder); StreamReader reader(decoderStream); while (!reader.IsEnd()) { lines.Add(reader.ReadLine()); } return true; } bool File::WriteAllText(const WString& text, bool bom, stream::BomEncoder::Encoding encoding) { FileStream fileStream(filePath.GetFullPath(), FileStream::WriteOnly); if (!fileStream.IsAvailable()) return false; IEncoder* encoder = nullptr; if (bom) { encoder = new BomEncoder(encoding); } else switch (encoding) { case BomEncoder::Utf8: encoder = new Utf8Encoder; break; case BomEncoder::Utf16: encoder = new Utf16Encoder; break; case BomEncoder::Utf16BE: encoder = new Utf16BEEncoder; break; default: encoder = new MbcsEncoder; break; } { EncoderStream encoderStream(fileStream, *encoder); StreamWriter writer(encoderStream); writer.WriteString(text); } delete encoder; return true; } bool File::WriteAllLines(collections::List& lines, bool bom, stream::BomEncoder::Encoding encoding) { FileStream fileStream(filePath.GetFullPath(), FileStream::WriteOnly); if (!fileStream.IsAvailable()) return false; IEncoder* encoder = nullptr; if (bom) { encoder = new BomEncoder(encoding); } else switch (encoding) { case BomEncoder::Utf8: encoder = new Utf8Encoder; break; case BomEncoder::Utf16: encoder = new Utf16Encoder; break; case BomEncoder::Utf16BE: encoder = new Utf16BEEncoder; break; default: encoder = new MbcsEncoder; break; } { EncoderStream encoderStream(fileStream, *encoder); StreamWriter writer(encoderStream); FOREACH(WString, line, lines) { writer.WriteLine(line); } } delete encoder; return true; } bool File::Exists()const { 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(); } bool Folder::Create(bool recursively)const { if (recursively) { auto folder = filePath.GetFolder(); if (folder.IsFile()) return false; if (folder.IsFolder()) return Create(false); return Folder(folder).Create(true) && Create(false); } 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 } } bool Folder::Delete(bool recursively)const { if (!Exists()) return false; if (recursively) { List folders; GetFolders(folders); FOREACH(Folder, folder, folders) { if (!folder.Delete(true)) return false; } List files; GetFiles(files); FOREACH(File, file, files) { if (!file.Delete()) return false; } 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 } } } /*********************************************************************** .\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(reading, (index1 ? index1 : index2) - reading); port = INTERNET_DEFAULT_HTTPS_PORT; secure = true; if (index1) { WString portString(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(reading, (index1 ? index1 : index2) - reading); port = INTERNET_DEFAULT_HTTP_PORT; if (index1) { WString portString(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(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; FOREACH(BufferPair, p, availableBuffers) { totalSize += p.length; } response.body.Resize(totalSize); if (totalSize > 0) { char* utf8 = new char[totalSize]; { char* temp = utf8; FOREACH(BufferPair, p, availableBuffers) { memcpy(temp, p.buffer, p.length); temp += p.length; } } memcpy(&response.body[0], utf8, totalSize); delete[] utf8; } FOREACH(BufferPair, 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 ***********************************************************************/ /*********************************************************************** 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 ***********************************************************************/ Locale::Locale(const WString& _localeName) :localeName(_localeName) { } Locale::~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 += *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()); } #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; } #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; } #endif } } /*********************************************************************** .\THREADING.CPP ***********************************************************************/ /*********************************************************************** 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 { void ThreadLocalStorage::Clear() { CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Clear()#Cannot access a disposed ThreadLocalStorage."); if(destructor) { if (auto data = Get()) { destructor(data); } } Set(nullptr); } void ThreadLocalStorage::Dispose() { CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Dispose()#Cannot access a disposed ThreadLocalStorage."); Clear(); disposed = true; } struct TlsStorageLink { ThreadLocalStorage* storage = nullptr; TlsStorageLink* next = nullptr; }; volatile bool tlsFixed = false; TlsStorageLink* tlsHead = nullptr; TlsStorageLink** tlsTail = &tlsHead; void ThreadLocalStorage::PushStorage(ThreadLocalStorage* storage) { CHECK_ERROR(!tlsFixed, L"vl::ThreadLocalStorage::PushStorage(ThreadLocalStorage*)#Cannot create new ThreadLocalStorage instance after calling ThreadLocalStorage::FixStorages()."); auto link = new TlsStorageLink; link->storage = storage; *tlsTail = link; tlsTail = &link->next; } void ThreadLocalStorage::FixStorages() { tlsFixed = true; } void ThreadLocalStorage::ClearStorages() { FixStorages(); auto current = tlsHead; while (current) { current->storage->Clear(); current = current->next; } } void ThreadLocalStorage::DisposeStorages() { FixStorages(); auto current = tlsHead; tlsHead = nullptr; tlsTail = nullptr; while (current) { current->storage->Dispose(); auto temp = current; current = current->next; delete temp; } } } /*********************************************************************** .\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 { internalData->counter++; internalData->cond.SleepWith(internalData->mutex); 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 ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #include namespace vl { namespace stream { using namespace collections; /*********************************************************************** TextReader ***********************************************************************/ WString TextReader::ReadString(vint length) { wchar_t* buffer=new wchar_t[length+1]; vint i=0; for(;i0 && result[result.Length()-1]==L'\r') { return result.Left(result.Length()-1); } else { return result; } } WString TextReader::ReadToEnd() { WString result; auto buffer = new wchar_t[65537]; buffer[0]=L'\0'; vint i=0; while(true) { wchar_t c=ReadChar(); if(c==L'\0') { buffer[i]=L'\0'; result+=buffer; buffer[0]=L'\0'; i=0; break; } else { if(i==65536) { buffer[i]=L'\0'; result+=buffer; buffer[0]=L'\0'; i=0; } buffer[i++]=c; } } result+=buffer; delete[] buffer; return result; } /*********************************************************************** TextWriter ***********************************************************************/ void TextWriter::WriteString(const wchar_t* string, vint charCount) { while(*string) { WriteChar(*string++); } } void TextWriter::WriteString(const wchar_t* string) { WriteString(string, (vint)wcslen(string)); } void TextWriter::WriteString(const WString& string) { if(string.Length()) { WriteString(string.Buffer(), string.Length()); } } void TextWriter::WriteLine(const wchar_t* string, vint charCount) { WriteString(string, charCount); WriteString(L"\r\n", 2); } void TextWriter::WriteLine(const wchar_t* string) { WriteString(string); WriteString(L"\r\n", 2); } void TextWriter::WriteLine(const WString& string) { WriteString(string); WriteString(L"\r\n", 2); } namespace monospace_tabling { void WriteBorderLine(TextWriter& writer, Array& columnWidths, vint columns) { writer.WriteChar(L'+'); for(vint i=0;i& columnWidths, vint rowHeight, vint columns, Array& tableByRow, vint startRow) { vint cellStart=startRow*columns; for(vint r=0;r=length;i--) { writer.WriteChar(L' '); } writer.WriteChar(L'|'); } writer.WriteLine(L""); } } } using namespace monospace_tabling; void TextWriter::WriteMonospacedEnglishTable(collections::Array& tableByRow, vint rows, vint columns) { Array rowHeights(rows); Array columnWidths(columns); for(vint i=0;iremain) length=remain; WString result=string.Sub(current, length); current+=length; return result; } } WString StringReader::ReadLine() { PrepareIfLastCallIsReadLine(); if(IsEnd()) { return L""; } else { vint lineEnd=current; while(lineEndRead(&buffer, sizeof(buffer))==0) { stream=0; return 0; } else { return buffer; } } else { return L'\0'; } } /*********************************************************************** StreamWriter ***********************************************************************/ StreamWriter::StreamWriter(IStream& _stream) :stream(&_stream) { } void StreamWriter::WriteChar(wchar_t c) { stream->Write(&c, sizeof(c)); } void StreamWriter::WriteString(const wchar_t* string, vint charCount) { stream->Write((void*)string, charCount*sizeof(*string)); } /*********************************************************************** EncoderStream ***********************************************************************/ EncoderStream::EncoderStream(IStream& _stream, IEncoder& _encoder) :stream(&_stream) ,encoder(&_encoder) ,position(0) { encoder->Setup(stream); } EncoderStream::~EncoderStream() { Close(); } bool EncoderStream::CanRead()const { return false; } bool EncoderStream::CanWrite()const { return IsAvailable(); } bool EncoderStream::CanSeek()const { return false; } bool EncoderStream::CanPeek()const { return false; } bool EncoderStream::IsLimited()const { return stream!=0 && stream->IsLimited(); } bool EncoderStream::IsAvailable()const { return stream!=0 && stream->IsAvailable(); } void EncoderStream::Close() { encoder->Close(); stream=0; } pos_t EncoderStream::Position()const { return IsAvailable()?position:-1; } pos_t EncoderStream::Size()const { return -1; } void EncoderStream::Seek(pos_t _size) { CHECK_FAIL(L"EncoderStream::Seek(pos_t)#Operation not supported."); } void EncoderStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"EncoderStream::SeekFromBegin(pos_t)#Operation not supported."); } void EncoderStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"EncoderStream::SeekFromEnd(pos_t)#Operation not supported."); } vint EncoderStream::Read(void* _buffer, vint _size) { CHECK_FAIL(L"EncoderStream::Read(void*, vint)#Operation not supported."); } vint EncoderStream::Write(void* _buffer, vint _size) { vint result=encoder->Write(_buffer, _size); if(result>=0) { position+=result; } return result; } vint EncoderStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"EncoderStream::Peek(void*, vint)#Operation not supported."); } /*********************************************************************** DecoderStream ***********************************************************************/ DecoderStream::DecoderStream(IStream& _stream, IDecoder& _decoder) :stream(&_stream) ,decoder(&_decoder) ,position(0) { decoder->Setup(stream); } DecoderStream::~DecoderStream() { Close(); } bool DecoderStream::CanRead()const { return IsAvailable(); } bool DecoderStream::CanWrite()const { return false; } bool DecoderStream::CanSeek()const { return false; } bool DecoderStream::CanPeek()const { return false; } bool DecoderStream::IsLimited()const { return stream!=0 && stream->IsLimited(); } bool DecoderStream::IsAvailable()const { return stream!=0 && stream->IsAvailable(); } void DecoderStream::Close() { decoder->Close(); stream=0; } pos_t DecoderStream::Position()const { return IsAvailable()?position:-1; } pos_t DecoderStream::Size()const { return -1; } void DecoderStream::Seek(pos_t _size) { CHECK_FAIL(L"DecoderStream::Seek(pos_t)#Operation not supported."); } void DecoderStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"DecoderStream::SeekFromBegin(pos_t)#Operation not supported."); } void DecoderStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"DecoderStream::SeekFromEnd(pos_t)#Operation not supported."); } vint DecoderStream::Read(void* _buffer, vint _size) { vint result=decoder->Read(_buffer, _size); if(result>=0) { position+=result; } return result; } vint DecoderStream::Write(void* _buffer, vint _size) { CHECK_FAIL(L"DecoderStream::Write(void*, vint)#Operation not supported."); } vint DecoderStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"DecoderStream::Peek(void*, vint)#Operation not supported."); } } } /*********************************************************************** .\STREAM\BROADCASTSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** BroadcastStream ***********************************************************************/ BroadcastStream::BroadcastStream() :closed(false) ,position(0) { } BroadcastStream::~BroadcastStream() { } BroadcastStream::StreamList& BroadcastStream::Targets() { return streams; } bool BroadcastStream::CanRead()const { return false; } bool BroadcastStream::CanWrite()const { return !closed; } bool BroadcastStream::CanSeek()const { return false; } bool BroadcastStream::CanPeek()const { return false; } bool BroadcastStream::IsLimited()const { return false; } bool BroadcastStream::IsAvailable()const { return !closed; } void BroadcastStream::Close() { closed=true; position=-1; } pos_t BroadcastStream::Position()const { return position; } pos_t BroadcastStream::Size()const { return position; } void BroadcastStream::Seek(pos_t _size) { CHECK_FAIL(L"BroadcastStream::Seek(pos_t)#Operation not supported."); } void BroadcastStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"BroadcastStream::SeekFromBegin(pos_t)#Operation not supported."); } void BroadcastStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"BroadcastStream::SeekFromEnd(pos_t)#Operation not supported."); } vint BroadcastStream::Read(void* _buffer, vint _size) { CHECK_FAIL(L"BroadcastStream::Read(void*, vint)#Operation not supported."); } vint BroadcastStream::Write(void* _buffer, vint _size) { for(vint i=0;iWrite(_buffer, _size); CHECK_ERROR(written == _size, L"BroadcastStream::Write(void*, vint)#Failed to copy data to the output stream."); } position+=_size; return _size; } vint BroadcastStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"BroadcastStream::Peek(void*, vint)#Operation not supported."); } } } /*********************************************************************** .\STREAM\CACHESTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** CacheStream ***********************************************************************/ void CacheStream::Flush() { if(dirtyLength>0) { if(target->Position()!=start+dirtyStart) { target->SeekFromBegin(start+dirtyStart); } target->Write(buffer+dirtyStart, dirtyLength); } dirtyStart=0; dirtyLength=0; availableLength=0; } void CacheStream::Load(pos_t _position) { if(target->Position()!=_position) { target->SeekFromBegin(_position); } start=_position; if(target->CanRead()) { availableLength=target->Read(buffer, block); } } vint CacheStream::InternalRead(void* _buffer, vint _size) { vint readed=0; if(position>=start && positionreaded) { Flush(); if(_size-readed>=block) { if(CanSeek()) { target->SeekFromBegin(position+readed); } vint additional=target->Read(_buffer, _size-readed); if(additional!=-1) { readed+=additional; } } else { Load(position+readed); vint remain=_size-readed; vint min=availableLength=start && position0) { availableLength+=availableOffset; } } if(_size>written) { Flush(); if(_size-written>=block) { if(CanSeek()) { target->SeekFromBegin(position+written); } vint additional=target->Write(_buffer, _size-written); if(additional!=-1) { written+=additional; } } else { Load(position+written); dirtyLength=_size-written; memcpy(buffer, _buffer, dirtyLength); written+=dirtyLength; } } return written; } CacheStream::CacheStream(IStream& _target, vint _block) :target(&_target) ,block(_block) ,start(0) ,position(0) ,dirtyStart(0) ,dirtyLength(0) ,availableLength(0) ,operatedSize(0) { if(block<=0) { block=65536; } buffer=new char[block]; } CacheStream::~CacheStream() { Close(); } bool CacheStream::CanRead()const { return target!=0 && target->CanRead(); } bool CacheStream::CanWrite()const { return target!=0 && target->CanWrite(); } bool CacheStream::CanSeek()const { return target!=0 && target->CanSeek(); } bool CacheStream::CanPeek()const { return target!=0 && target->CanPeek(); } bool CacheStream::IsLimited()const { return target!=0 && target->IsLimited(); } bool CacheStream::IsAvailable()const { return target!=0 && target->IsAvailable(); } void CacheStream::Close() { Flush(); target=0; delete[] buffer; buffer=0; position=-1; dirtyStart=0; dirtyLength=0; availableLength=0; operatedSize=-1; } pos_t CacheStream::Position()const { return position; } pos_t CacheStream::Size()const { if(target!=0) { if(IsLimited()) { return target->Size(); } else { return operatedSize; } } else { return -1; } } void CacheStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void CacheStream::SeekFromBegin(pos_t _size) { if(CanSeek()) { if(_size<0) { position=0; } else if(_size>Size()) { position=Size(); } else { position=_size; } } } void CacheStream::SeekFromEnd(pos_t _size) { SeekFromBegin(Size()-_size); } vint CacheStream::Read(void* _buffer, vint _size) { CHECK_ERROR(CanRead(), L"CacheStream::Read(void*, vint)#Stream is closed or operation not supported."); CHECK_ERROR(_size>=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); _size=InternalRead(_buffer, _size); position+=_size; if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); if(IsLimited()) { pos_t size=Size(); if(size!=-1) { vint remain=(vint)(size-(position+_size)); if(remain<0) { _size-=remain; } } } _size=InternalWrite(_buffer, _size); position+=_size; if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); return InternalRead(_buffer, _size); } } } /*********************************************************************** .\STREAM\CHARFORMAT.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #if defined VCZH_MSVC #include #elif defined VCZH_GCC #endif namespace vl { namespace stream { /*********************************************************************** CharEncoder ***********************************************************************/ CharEncoder::CharEncoder() :stream(0) ,cacheSize(0) { } void CharEncoder::Setup(IStream* _stream) { stream=_stream; } void CharEncoder::Close() { } vint CharEncoder::Write(void* _buffer, vint _size) { const vint all=cacheSize+_size; const vint chars=all/sizeof(wchar_t); const vint bytes=chars*sizeof(wchar_t); wchar_t* unicode=0; bool needToFree=false; vint result=0; if(chars) { if(cacheSize>0) { unicode=new wchar_t[chars]; memcpy(unicode, cacheBuffer, cacheSize); memcpy(((vuint8_t*)unicode)+cacheSize, _buffer, bytes-cacheSize); needToFree=true; } else { unicode=(wchar_t*)_buffer; } result=WriteString(unicode, chars)*sizeof(wchar_t)-cacheSize; cacheSize=0; } if(needToFree) { delete[] unicode; } if(all-bytes>0) { cacheSize=all-bytes; memcpy(cacheBuffer, (vuint8_t*)_buffer+_size-cacheSize, cacheSize); result+=cacheSize; } return result; } /*********************************************************************** CharDecoder ***********************************************************************/ CharDecoder::CharDecoder() :stream(0) ,cacheSize(0) { } void CharDecoder::Setup(IStream* _stream) { stream=_stream; } void CharDecoder::Close() { } vint CharDecoder::Read(void* _buffer, vint _size) { vuint8_t* unicode=(vuint8_t*)_buffer; vint result=0; { vint index=0; while(cacheSize>0 && _size>0) { *unicode++=cacheBuffer[index]++; cacheSize--; _size--; result++; } } const vint chars=_size/sizeof(wchar_t); vint bytes=ReadString((wchar_t*)unicode, chars)*sizeof(wchar_t); result+=bytes; _size-=bytes; unicode+=bytes; if(_size>0) { wchar_t c; if(ReadString(&c, 1)==1) { cacheSize=sizeof(wchar_t)-_size; memcpy(unicode, &c, _size); memcpy(cacheBuffer, (vuint8_t*)&c+_size, cacheSize); result+=_size; } } return result; } /*********************************************************************** Mbcs ***********************************************************************/ vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars) { #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(_buffer, chars); AString a=wtoa(w); vint length=a.Length(); vint result=stream->Write((void*)a.Buffer(), length); #endif if(result==length) { return chars; } else { Close(); return 0; } } vint MbcsDecoder::ReadString(wchar_t* _buffer, vint chars) { char* source=new char[chars*2]; char* reading=source; vint readed=0; while(readedRead(reading, 1)!=1) { break; } #if defined VCZH_MSVC if(IsDBCSLeadByte(*reading)) #elif defined VCZH_GCC if((vint8_t)*reading<0) #endif { if(stream->Read(reading+1, 1)!=1) { break; } reading+=2; } else { reading++; } readed++; } #if defined VCZH_MSVC MultiByteToWideChar(CP_THREAD_ACP, 0, source, (int)(reading-source), _buffer, (int)chars); #elif defined VCZH_GCC AString a(source, (vint)(reading-source)); WString w=atow(a); memcpy(_buffer, w.Buffer(), readed*sizeof(wchar_t)); #endif delete[] source; return readed; } /*********************************************************************** Utf-16 ***********************************************************************/ vint Utf16Encoder::WriteString(wchar_t* _buffer, vint chars) { #if defined VCZH_MSVC return stream->Write(_buffer, chars*sizeof(wchar_t))/sizeof(wchar_t); #elif defined VCZH_GCC vint writed = 0; vuint16_t utf16 = 0; vuint8_t* utf16buf = (vuint8_t*)&utf16; while (writed < chars) { wchar_t w = *_buffer++; if (w < 0x10000) { utf16 = (vuint16_t)w; if (stream->Write(&utf16buf[0], 1) != 1) break; if (stream->Write(&utf16buf[1], 1) != 1) break; } else if (w < 0x110000) { wchar_t inc = w - 0x10000; utf16 = (vuint16_t)(inc / 0x400) + 0xD800; if (stream->Write(&utf16buf[0], 1) != 1) break; if (stream->Write(&utf16buf[1], 1) != 1) break; utf16 = (vuint16_t)(inc % 0x400) + 0xDC00; if (stream->Write(&utf16buf[0], 1) != 1) break; if (stream->Write(&utf16buf[1], 1) != 1) break; } else { break; } writed++; } if(writed!=chars) { Close(); } return writed; #endif } vint Utf16Decoder::ReadString(wchar_t* _buffer, vint chars) { #if defined VCZH_MSVC return stream->Read(_buffer, chars*sizeof(wchar_t))/sizeof(wchar_t); #elif defined VCZH_GCC wchar_t* writing = _buffer; while (writing - _buffer < chars) { vuint16_t utf16_1 = 0; vuint16_t utf16_2 = 0; if (stream->Read(&utf16_1, 2) != 2) break; if (utf16_1 < 0xD800 || utf16_1 > 0xDFFF) { *writing++ = (wchar_t)utf16_1; } else if (utf16_1 < 0xDC00) { if (stream->Read(&utf16_2, 2) != 2) break; if (0xDC00 <= utf16_2 && utf16_2 <= 0xDFFF) { *writing++ = (wchar_t)(utf16_1 - 0xD800) * 0x400 + (wchar_t)(utf16_2 - 0xDC00) + 0x10000; } else { break; } } else { break; } } return writing - _buffer; #endif } /*********************************************************************** Utf-16-be ***********************************************************************/ vint Utf16BEEncoder::WriteString(wchar_t* _buffer, vint chars) { #if defined VCZH_MSVC vint writed=0; while(writedWrite(((unsigned char*)_buffer)+1, 1)!=1) { break; } if(stream->Write(_buffer, 1)!=1) { break; } _buffer++; writed++; } if(writed!=chars) { Close(); } return writed; #elif defined VCZH_GCC vint writed = 0; vuint16_t utf16 = 0; vuint8_t* utf16buf = (vuint8_t*)&utf16; while (writed < chars) { wchar_t w = *_buffer++; if (w < 0x10000) { utf16 = (vuint16_t)w; if (stream->Write(&utf16buf[1], 1) != 1) break; if (stream->Write(&utf16buf[0], 1) != 1) break; } else if (w < 0x110000) { wchar_t inc = w - 0x10000; utf16 = (vuint16_t)(inc / 0x400) + 0xD800; if (stream->Write(&utf16buf[1], 1) != 1) break; if (stream->Write(&utf16buf[0], 1) != 1) break; utf16 = (vuint16_t)(inc % 0x400) + 0xDC00; if (stream->Write(&utf16buf[1], 1) != 1) break; if (stream->Write(&utf16buf[0], 1) != 1) break; } else { break; } writed++; } if(writed!=chars) { Close(); } return writed; #endif } vint Utf16BEDecoder::ReadString(wchar_t* _buffer, vint chars) { #if defined VCZH_MSVC chars=stream->Read(_buffer, chars*sizeof(wchar_t))/sizeof(wchar_t); unsigned char* unicode=(unsigned char*)_buffer; for(vint i=0;iRead(&utf16_1, 2) != 2) break; utf16buf = (vuint8_t*)&utf16_1; utf16buf_temp = utf16buf[0]; utf16buf[0] = utf16buf[1]; utf16buf[1] = utf16buf_temp; if (utf16_1 < 0xD800 || utf16_1 > 0xDFFF) { *writing++ = (wchar_t)utf16_1; } else if (utf16_1 < 0xDC00) { if (stream->Read(&utf16_2, 2) != 2) break; utf16buf = (vuint8_t*)&utf16_2; utf16buf_temp = utf16buf[0]; utf16buf[0] = utf16buf[1]; utf16buf[1] = utf16buf_temp; if (0xDC00 <= utf16_2 && utf16_2 <= 0xDFFF) { *writing++ = (wchar_t)(utf16_1 - 0xD800) * 0x400 + (wchar_t)(utf16_2 - 0xDC00) + 0x10000; } else { break; } } else { break; } } return writing - _buffer; #endif } /*********************************************************************** Utf8 ***********************************************************************/ vint Utf8Encoder::WriteString(wchar_t* _buffer, vint chars) { #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) { return chars; } else { Close(); return 0; } #elif defined VCZH_GCC vint writed = 0; while (writed < chars) { wchar_t w = *_buffer++; vuint8_t utf8[4]; if (w < 0x80) { utf8[0] = (vuint8_t)w; if (stream->Write(utf8, 1) != 1) break; } else if (w < 0x800) { utf8[0] = 0xC0 + ((w & 0x7C0) >> 6); utf8[1] = 0x80 + (w & 0x3F); if (stream->Write(utf8, 2) != 2) break; } else if (w < 0x10000) { utf8[0] = 0xE0 + ((w & 0xF000) >> 12); utf8[1] = 0x80 + ((w & 0xFC0) >> 6); utf8[2] = 0x80 + (w & 0x3F); if (stream->Write(utf8, 3) != 3) break; } else if (w < 0x110000) // only accept UTF-16 range { utf8[0] = 0xF0 + ((w & 0x1C0000) >> 18); utf8[1] = 0x80 + ((w & 0x3F000) >> 12); utf8[2] = 0x80 + ((w & 0xFC0) >> 6); utf8[3] = 0x80 + (w & 0x3F); if (stream->Write(utf8, 4) != 4) break; } else { break; } writed++; } if(writed!=chars) { Close(); } return writed; #endif } Utf8Decoder::Utf8Decoder() #if defined VCZH_MSVC :cache(0) ,cacheAvailable(false) #endif { } vint Utf8Decoder::ReadString(wchar_t* _buffer, vint chars) { vuint8_t source[4]; #if defined VCZH_MSVC wchar_t target[2]; #endif wchar_t* writing=_buffer; vint readed=0; vint sourceCount=0; while(readedRead(source, 1)!=1) { break; } if((*source & 0xF0) == 0xF0) { if(stream->Read(source+1, 3)!=3) { break; } sourceCount=4; } else if((*source & 0xE0) == 0xE0) { if(stream->Read(source+1, 2)!=2) { break; } sourceCount=3; } else if((*source & 0xC0) == 0xC0) { if(stream->Read(source+1, 1)!=1) { break; } sourceCount=2; } else { sourceCount=1; } #if defined VCZH_MSVC int targetCount=MultiByteToWideChar(CP_UTF8, 0, (char*)source, (int)sourceCount, target, 2); if(targetCount==1) { *writing++=target[0]; } else if(targetCount==2) { *writing++=target[0]; cache=target[1]; cacheAvailable=true; } else { break; } } #elif defined VCZH_GCC if (sourceCount == 1) { *writing++ = (wchar_t)source[0]; } else if (sourceCount == 2) { *writing++ = (((wchar_t)source[0] & 0x1F) << 6) + ((wchar_t)source[1] & 0x3F); } else if (sourceCount == 3) { *writing++ = (((wchar_t)source[0] & 0xF) << 12) + (((wchar_t)source[1] & 0x3F) << 6) + ((wchar_t)source[2] & 0x3F); } else if (sourceCount == 4) { *writing++ = (((wchar_t)source[0] & 0x7) << 18) + (((wchar_t)source[1] & 0x3F) << 12) + (((wchar_t)source[2] & 0x3F) << 6) + ((wchar_t)source[3] & 0x3F); } else { break; } #endif readed++; } return readed; } /*********************************************************************** BomEncoder ***********************************************************************/ BomEncoder::BomEncoder(Encoding _encoding) :encoding(_encoding) ,encoder(0) { switch(encoding) { case Mbcs: encoder=new MbcsEncoder; break; case Utf8: encoder=new Utf8Encoder; break; case Utf16: encoder=new Utf16Encoder; break; case Utf16BE: encoder=new Utf16BEEncoder; break; } } BomEncoder::~BomEncoder() { Close(); } void BomEncoder::Setup(IStream* _stream) { switch(encoding) { case Mbcs: break; case Utf8: _stream->Write((void*)"\xEF\xBB\xBF", 3); break; case Utf16: _stream->Write((void*)"\xFF\xFE", 2); break; case Utf16BE: _stream->Write((void*)"\xFE\xFF", 2); break; } encoder->Setup(_stream); } void BomEncoder::Close() { if(encoder) { encoder->Close(); delete encoder; encoder=0; } } vint BomEncoder::Write(void* _buffer, vint _size) { return encoder->Write(_buffer, _size); } /*********************************************************************** BomDecoder ***********************************************************************/ BomDecoder::BomStream::BomStream(IStream* _stream, char* _bom, vint _bomLength) :stream(_stream) ,bomPosition(0) ,bomLength(_bomLength) { memcpy(bom, _bom, bomLength); } bool BomDecoder::BomStream::CanRead()const { return IsAvailable(); } bool BomDecoder::BomStream::CanWrite()const { return false; } bool BomDecoder::BomStream::CanSeek()const { return false; } bool BomDecoder::BomStream::CanPeek()const { return false; } bool BomDecoder::BomStream::IsLimited()const { return stream!=0 && stream->IsLimited(); } bool BomDecoder::BomStream::IsAvailable()const { return stream!=0 && stream->IsAvailable(); } void BomDecoder::BomStream::Close() { stream=0; } pos_t BomDecoder::BomStream::Position()const { return IsAvailable()?bomPosition+stream->Position():-1; } pos_t BomDecoder::BomStream::Size()const { return -1; } void BomDecoder::BomStream::Seek(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::Seek(pos_t)#Operation not supported."); } void BomDecoder::BomStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::SeekFromBegin(pos_t)#Operation not supported."); } void BomDecoder::BomStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::SeekFromEnd(pos_t)#Operation not supported."); } vint BomDecoder::BomStream::Read(void* _buffer, vint _size) { vint result=0; unsigned char* buffer=(unsigned char*)_buffer; if(bomPositionRead(buffer, _size); } return result; } vint BomDecoder::BomStream::Write(void* _buffer, vint _size) { CHECK_FAIL(L"BomDecoder::BomStream::Write(void*, vint)#Operation not supported."); } vint BomDecoder::BomStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"BomDecoder::BomStream::Peek(void*, vint)#Operation not supported."); } BomDecoder::BomDecoder() :decoder(0) { } BomDecoder::~BomDecoder() { Close(); } void BomDecoder::Setup(IStream* _stream) { char bom[3]={0}; vint length=_stream->Read(bom, sizeof(bom)); if(strncmp(bom, "\xEF\xBB\xBF", 3)==0) { decoder=new Utf8Decoder; stream=new BomStream(_stream, bom+3, 0); } else if(strncmp(bom, "\xFF\xFE", 2)==0) { decoder=new Utf16Decoder; stream=new BomStream(_stream, bom+2, 1); } else if(strncmp(bom, "\xFE\xFF", 2)==0) { decoder=new Utf16BEDecoder; stream=new BomStream(_stream, bom+2, 1); } else { decoder=new MbcsDecoder; stream=new BomStream(_stream, bom, 3); } decoder->Setup(stream); } void BomDecoder::Close() { if(decoder) { decoder->Close(); delete decoder; decoder=0; stream->Close(); delete stream; stream=0; } } vint BomDecoder::Read(void* _buffer, vint _size) { return decoder->Read(_buffer, _size); } /*********************************************************************** CharEncoder ***********************************************************************/ bool CanBeMbcs(unsigned char* buffer, vint size) { for(vint i=0;i 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 void TestEncoding(unsigned char* buffer, vint size, BomEncoder::Encoding& encoding, bool& containsBom) { if (size >= 3 && strncmp((char*)buffer, "\xEF\xBB\xBF", 3) == 0) { encoding = BomEncoder::Utf8; containsBom = true; } else if (size >= 2 && strncmp((char*)buffer, "\xFF\xFE", 2) == 0) { encoding = BomEncoder::Utf16; containsBom = true; } else if (size >= 2 && strncmp((char*)buffer, "\xFE\xFF", 2) == 0) { encoding = BomEncoder::Utf16BE; containsBom = true; } else { encoding = BomEncoder::Mbcs; containsBom = false; bool utf16HitSurrogatePairs = false; bool utf16BEHitSurrogatePairs = false; bool roughMbcs = CanBeMbcs(buffer, size); bool roughUtf8 = CanBeUtf8(buffer, size); bool roughUtf16 = CanBeUtf16(buffer, size, utf16HitSurrogatePairs); bool roughUtf16BE = CanBeUtf16BE(buffer, size, utf16BEHitSurrogatePairs); vint roughCount = (roughMbcs ? 1 : 0) + (roughUtf8 ? 1 : 0) + (roughUtf16 ? 1 : 0) + (roughUtf16BE ? 1 : 0); if (roughCount == 1) { if (roughUtf8) encoding = BomEncoder::Utf8; else if (roughUtf16) encoding = BomEncoder::Utf16; else if (roughUtf16BE) encoding = BomEncoder::Utf16BE; } 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 } } } } } /*********************************************************************** .\STREAM\COMPRESSIONSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { using namespace collections; using namespace lzw; /*********************************************************************** LzwBase ***********************************************************************/ void LzwBase::UpdateIndexBits() { if (nextIndex >=2 && (nextIndex & (nextIndex - 1)) == 0) { indexBits++; } } lzw::Code* LzwBase::CreateCode(lzw::Code* prefix, vuint8_t byte) { if (nextIndex < MaxDictionarySize) { Code* code = codeAllocator.Create(); code->byte = byte; code->code = nextIndex; code->parent = prefix; code->size = prefix->size + 1; prefix->children.Set(byte, code, mapAllocator); nextIndex++; return code; } else { return 0; } } LzwBase::LzwBase() :codeAllocator(65536) , mapAllocator(1048576) { root = codeAllocator.Create(); for (vint i = 0; i < 256; i++) { UpdateIndexBits(); CreateCode(root, (vuint8_t)i); } } LzwBase::LzwBase(bool (&existingBytes)[256]) { root = codeAllocator.Create(); for (vint i = 0; i < 256; i++) { if (existingBytes[i]) { UpdateIndexBits(); CreateCode(root, (vuint8_t)i); } } if (indexBits < 8) { eofIndex = nextIndex++; } } LzwBase::~LzwBase() { } /*********************************************************************** LzwEncoder ***********************************************************************/ void LzwEncoder::Flush() { vint written = 0; vint bufferUsedSize = bufferUsedBits / 8; if (bufferUsedBits % 8 != 0) { bufferUsedSize++; } while (written < bufferUsedSize) { vint size = stream->Write(buffer + written, bufferUsedSize - written); CHECK_ERROR(size != 0, L"LzwEncoder::Flush()#Failed to flush the lzw buffer."); written += size; } bufferUsedBits = 0; } vuint8_t highMarks[9] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF }; vuint8_t lowMarks[9] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF }; void LzwEncoder::WriteNumber(vint number, vint bitSize) { vint bitStart = 0; vint bitStep = 8 - bufferUsedBits % 8; if (bitStep > bitSize) { bitStep = bitSize; } while (bitStart < bitSize) { if(bufferUsedBits == BufferSize * 8) { Flush(); } vint writeStart = bufferUsedBits % 8; vint byteIndex = bufferUsedBits / 8; vuint8_t byte = buffer[byteIndex]; byte &= highMarks[writeStart]; vuint8_t content = (vuint8_t)((number >> bitStart)&lowMarks[bitStep]) << (8 - writeStart - bitStep); byte |= content; buffer[byteIndex] = byte; bufferUsedBits += bitStep; bitStart += bitStep; vint remain = bitSize - bitStart; bitStep = remain < 8 ? remain : 8; } } LzwEncoder::LzwEncoder() { prefix = root; } LzwEncoder::LzwEncoder(bool (&existingBytes)[256]) :LzwBase(existingBytes) { prefix = root; } LzwEncoder::~LzwEncoder() { } void LzwEncoder::Setup(IStream* _stream) { stream = _stream; } void LzwEncoder::Close() { if (prefix != root) { WriteNumber(prefix->code, indexBits); prefix = root; } vint remain = 8 - bufferUsedBits % 8; if (remain != 8 && remain >= indexBits) { CHECK_ERROR(eofIndex != -1, L"LzwEncoder::Close()#Internal error."); WriteNumber(eofIndex, indexBits); } Flush(); } vint LzwEncoder::Write(void* _buffer, vint _size) { vuint8_t* bytes = (vuint8_t*)_buffer; for (vint i = 0; i < _size; i++) { vuint8_t byte = bytes[i]; Code* next = prefix->children.Get(byte); if (next) { prefix = next; } else { WriteNumber(prefix->code, indexBits); if (nextIndex < MaxDictionarySize) { UpdateIndexBits(); CreateCode(prefix, byte); } prefix = root->children.Get(byte); } } return _size; } /*********************************************************************** LzwDecoder ***********************************************************************/ bool LzwDecoder::ReadNumber(vint& number, vint bitSize) { number = 0; if (inputBufferSize == -1) { return false; } vint remainBits = inputBufferSize * 8 - inputBufferUsedBits; vint writtenBits = 0; vint bitStep = 8 - inputBufferUsedBits % 8; if (bitStep > bitSize) { bitStep = bitSize; } while (writtenBits < bitSize) { if (remainBits == 0) { inputBufferSize = stream->Read(inputBuffer, BufferSize); if (inputBufferSize == 0) { inputBufferSize = -1; return false; } remainBits = inputBufferSize * 8; inputBufferUsedBits = 0; } vuint8_t byte = inputBuffer[inputBufferUsedBits / 8]; byte >>= (8 - inputBufferUsedBits % 8 - bitStep); byte &= lowMarks[bitStep]; number |= byte << writtenBits; inputBufferUsedBits += bitStep; remainBits -= bitStep; writtenBits += bitStep; vint remain = bitSize - writtenBits; bitStep = remain < 8 ? remain : 8; } return true; } void LzwDecoder::PrepareOutputBuffer(vint size) { if (outputBuffer.Count() < size) { outputBuffer.Resize(size); } outputBufferSize = size; } void LzwDecoder::ExpandCodeToOutputBuffer(lzw::Code* code) { vuint8_t* outputByte = &outputBuffer[0] + code->size; Code* current = code; while (current != root) { *(--outputByte) = current->byte; current = current->parent; } outputBufferUsedBytes = 0; } LzwDecoder::LzwDecoder() { for (vint i = 0; i < 256; i++) { dictionary.Add(root->children.Get((vuint8_t)i)); } } LzwDecoder::LzwDecoder(bool (&existingBytes)[256]) :LzwBase(existingBytes) { for (vint i = 0; i < 256; i++) { if (existingBytes[i]) { dictionary.Add(root->children.Get((vuint8_t)i)); } } if (eofIndex != -1) { dictionary.Add(0); } } LzwDecoder::~LzwDecoder() { } void LzwDecoder::Setup(IStream* _stream) { stream = _stream; } void LzwDecoder::Close() { } vint LzwDecoder::Read(void* _buffer, vint _size) { vint written = 0; vuint8_t* bytes = (vuint8_t*)_buffer; while (written < _size) { vint expect = _size - written; vint remain = outputBufferSize - outputBufferUsedBytes; if (remain == 0) { vint index = 0; if (!ReadNumber(index, indexBits) || index == eofIndex) { break; } Code* prefix = 0; if (index == dictionary.Count()) { prefix = lastCode; PrepareOutputBuffer(prefix->size + 1); ExpandCodeToOutputBuffer(prefix); outputBuffer[outputBufferSize - 1] = outputBuffer[0]; } else { prefix = dictionary[index]; PrepareOutputBuffer(prefix->size); ExpandCodeToOutputBuffer(prefix); } if (nextIndex < MaxDictionarySize) { if (lastCode) { dictionary.Add(CreateCode(lastCode, outputBuffer[0])); } UpdateIndexBits(); } lastCode = dictionary[index]; } else { if (remain > expect) { remain = expect; } memcpy(bytes + written, &outputBuffer[outputBufferUsedBytes], remain); outputBufferUsedBytes += remain; written += remain; } } return written; } /*********************************************************************** Helper Functions ***********************************************************************/ vint CopyStream(stream::IStream& inputStream, stream::IStream& outputStream) { vint totalSize = 0; while (true) { char buffer[1024]; vint copied = inputStream.Read(buffer, (vint)sizeof(buffer)); if (copied == 0) { break; } totalSize += outputStream.Write(buffer, copied); } return totalSize; } const vint CompressionFragmentSize = 1048576; void CompressStream(stream::IStream& inputStream, stream::IStream& outputStream) { Array buffer(CompressionFragmentSize); while (true) { vint size = inputStream.Read(&buffer[0], buffer.Count()); if (size == 0) break; MemoryStream compressedStream; { LzwEncoder encoder; EncoderStream encoderStream(compressedStream, encoder); encoderStream.Write(&buffer[0], size); } compressedStream.SeekFromBegin(0); { { vint32_t bufferSize = (vint32_t)size; outputStream.Write(&bufferSize, (vint)sizeof(bufferSize)); } { vint32_t compressedSize = (vint32_t)compressedStream.Size(); outputStream.Write(&compressedSize, (vint)sizeof(compressedSize)); } CopyStream(compressedStream, outputStream); } } } void DecompressStream(stream::IStream& inputStream, stream::IStream& outputStream) { vint totalSize = 0; vint totalWritten = 0; while (true) { vint32_t bufferSize = 0; if (inputStream.Read(&bufferSize, (vint)sizeof(bufferSize)) != sizeof(bufferSize)) { break; } vint32_t compressedSize = 0; CHECK_ERROR(inputStream.Read(&compressedSize, (vint)sizeof(compressedSize)) == sizeof(compressedSize), L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); Array buffer(compressedSize); CHECK_ERROR(inputStream.Read(&buffer[0], compressedSize) == compressedSize, L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); MemoryWrapperStream compressedStream(&buffer[0], compressedSize); LzwDecoder decoder; DecoderStream decoderStream(compressedStream, decoder); totalWritten += CopyStream(decoderStream, outputStream); totalSize += bufferSize; } CHECK_ERROR(outputStream.Size() == totalSize, L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); } } } /*********************************************************************** .\STREAM\FILESTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #if defined VCZH_GCC #endif namespace vl { namespace stream { #if defined VCZH_GCC void _fseeki64(FILE* file, pos_t offset, int origin) { fseek(file, (long)offset, origin); } #endif /*********************************************************************** FileStream ***********************************************************************/ FileStream::FileStream(const WString& fileName, AccessRight _accessRight) :accessRight(_accessRight) { const wchar_t* mode=L"rb"; switch(accessRight) { case ReadOnly: mode=L"rb"; break; case WriteOnly: mode=L"wb"; break; case ReadWrite: mode=L"w+b"; break; } #if defined VCZH_MSVC if(_wfopen_s(&file, fileName.Buffer(), mode)!=0) { file=0; } #elif defined VCZH_GCC AString fileNameA = wtoa(fileName); AString modeA = wtoa(mode); file = fopen(fileNameA.Buffer(), modeA.Buffer()); #endif } FileStream::~FileStream() { Close(); } bool FileStream::CanRead()const { return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); } bool FileStream::CanWrite()const { return file!=0 && (accessRight==WriteOnly || accessRight==ReadWrite); } bool FileStream::CanSeek()const { return file!=0; } bool FileStream::CanPeek()const { return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); } bool FileStream::IsLimited()const { return file!=0 && accessRight==ReadOnly; } bool FileStream::IsAvailable()const { return file!=0; } void FileStream::Close() { if(file!=0) { fclose(file); file=0; } } pos_t FileStream::Position()const { if(file!=0) { #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { return position; } #elif defined VCZH_GCC return (pos_t)ftell(file); #endif } return -1; } pos_t FileStream::Size()const { if(file!=0) { #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { if(fseek(file, 0, SEEK_END)==0) { pos_t size=Position(); if(fsetpos(file, &position)==0) { return size; } } } #elif defined VCZH_GCC long position = ftell(file); fseek(file, 0, SEEK_END); long size=ftell(file); fseek(file, position, SEEK_SET); return (pos_t)size; #endif } return -1; } void FileStream::Seek(pos_t _size) { if(Position()+_size>Size()) { _fseeki64(file, 0, SEEK_END); } else if(Position()+_size<0) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, _size, SEEK_CUR); } } void FileStream::SeekFromBegin(pos_t _size) { if(_size>Size()) { _fseeki64(file, 0, SEEK_END); } else if(_size<0) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, _size, SEEK_SET); } } void FileStream::SeekFromEnd(pos_t _size) { if(_size<0) { _fseeki64(file, 0, SEEK_END); } else if(_size>Size()) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, -_size, SEEK_END); } } vint FileStream::Read(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Read(void*, vint)#Argument size cannot be negative."); return fread(_buffer, 1, _size, file); } vint FileStream::Write(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Write(void*, vint)#Argument size cannot be negative."); return fwrite(_buffer, 1, _size, file); } vint FileStream::Peek(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Peek(void*, vint)#Argument size cannot be negative."); #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { size_t count=fread(_buffer, 1, _size, file); if(fsetpos(file, &position)==0) { return count; } } return -1; #elif defined VCZH_GCC long position=ftell(file); size_t count=fread(_buffer, 1, _size, file); fseek(file, position, SEEK_SET); return count; #endif } } } /*********************************************************************** .\STREAM\MEMORYSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** MemoryStream ***********************************************************************/ void MemoryStream::PrepareSpace(vint totalSpace) { if(totalSpace>capacity) { totalSpace=(totalSpace/block+1)*block; char* newBuffer=new char[totalSpace]; if(buffer) { memcpy(newBuffer, buffer, size); delete[] buffer; } buffer=newBuffer; capacity=totalSpace; } } MemoryStream::MemoryStream(vint _block) :block(_block) ,buffer(0) ,size(0) ,position(0) ,capacity(0) { if(block<=0) { block=65536; } } MemoryStream::~MemoryStream() { Close(); } bool MemoryStream::CanRead()const { return block!=0; } bool MemoryStream::CanWrite()const { return block!=0; } bool MemoryStream::CanSeek()const { return block!=0; } bool MemoryStream::CanPeek()const { return block!=0; } bool MemoryStream::IsLimited()const { return false; } bool MemoryStream::IsAvailable()const { return block!=0; } void MemoryStream::Close() { if(buffer) { delete[] buffer; } block=0; buffer=0; size=-1; position=-1; capacity=0; } pos_t MemoryStream::Position()const { return position; } pos_t MemoryStream::Size()const { return size; } void MemoryStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void MemoryStream::SeekFromBegin(pos_t _size) { CHECK_ERROR(block!=0, L"MemoryStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); vint expected=(vint)_size; if(expected<0) { position=0; } else if(expected>=size) { position=size; } else { position=expected; } } void MemoryStream::SeekFromEnd(pos_t _size) { SeekFromBegin(size-_size); } vint MemoryStream::Read(void* _buffer, vint _size) { CHECK_ERROR(block!=0, L"MemoryStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryStream::Read(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); position+=_size; return _size; } vint MemoryStream::Write(void* _buffer, vint _size) { CHECK_ERROR(block!=0, L"MemoryStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryStream::Write(void*, vint)#Argument size cannot be negative."); PrepareSpace(size+_size); memmove(buffer+position, _buffer, _size); position+=_size; if(size=0, L"MemoryStream::Peek(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); return _size; } void* MemoryStream::GetInternalBuffer() { return buffer; } } } /*********************************************************************** .\STREAM\MEMORYWRAPPERSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** MemoryWrapperStream ***********************************************************************/ MemoryWrapperStream::MemoryWrapperStream(void* _buffer, vint _size) :buffer((char*)_buffer) ,size(_size) ,position(0) { if(size<=0) { buffer=0; size=0; } } MemoryWrapperStream::~MemoryWrapperStream() { } bool MemoryWrapperStream::CanRead()const { return buffer!=0; } bool MemoryWrapperStream::CanWrite()const { return buffer!=0; } bool MemoryWrapperStream::CanSeek()const { return buffer!=0; } bool MemoryWrapperStream::CanPeek()const { return buffer!=0; } bool MemoryWrapperStream::IsLimited()const { return buffer!=0; } bool MemoryWrapperStream::IsAvailable()const { return buffer!=0; } void MemoryWrapperStream::Close() { buffer=0; size=-1; position=-1; } pos_t MemoryWrapperStream::Position()const { return position; } pos_t MemoryWrapperStream::Size()const { return size; } void MemoryWrapperStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void MemoryWrapperStream::SeekFromBegin(pos_t _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); vint expected=(vint)_size; if(expected<0) { position=0; } else if(expected>=size) { position=size; } else { position=expected; } } void MemoryWrapperStream::SeekFromEnd(pos_t _size) { SeekFromBegin(size-_size); } vint MemoryWrapperStream::Read(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Read(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); position+=_size; return _size; } vint MemoryWrapperStream::Write(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Write(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(buffer+position, _buffer, _size); position+=_size; return _size; } vint MemoryWrapperStream::Peek(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Peek(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); return _size; } } } /*********************************************************************** .\STREAM\RECORDERSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** RecorderStream ***********************************************************************/ RecorderStream::RecorderStream(IStream& _in, IStream& _out) :in(&_in) , out(&_out) { } RecorderStream::~RecorderStream() { } bool RecorderStream::CanRead()const { return IsAvailable() && in->CanRead(); } bool RecorderStream::CanWrite()const { return false; } bool RecorderStream::CanSeek()const { return false; } bool RecorderStream::CanPeek()const { return false; } bool RecorderStream::IsLimited()const { return IsAvailable() && in->IsLimited(); } bool RecorderStream::IsAvailable()const { return in != 0 && out != 0 && in->IsAvailable() && out->IsAvailable(); } void RecorderStream::Close() { in = nullptr; out = nullptr; } pos_t RecorderStream::Position()const { return IsAvailable() ? in->Position() : -1; } pos_t RecorderStream::Size()const { return IsAvailable() ? in->Size() : -1; } void RecorderStream::Seek(pos_t _size) { CHECK_FAIL(L"RecorderStream::Seek(pos_t)#Operation not supported."); } void RecorderStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"RecorderStream::SeekFromBegin(pos_t)#Operation not supported."); } void RecorderStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"RecorderStream::SeekFromEnd(pos_t)#Operation not supported."); } vint RecorderStream::Read(void* _buffer, vint _size) { _size = in->Read(_buffer, _size); vint written = out->Write(_buffer, _size); CHECK_ERROR(written == _size, L"RecorderStream::Read(void*, vint)#Failed to copy data to the output stream."); return _size; } vint RecorderStream::Write(void* _buffer, vint _size) { CHECK_FAIL(L"RecorderStream::Write(void*, vint)#Operation not supported."); } vint RecorderStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"RecorderStream::Peek(void*, vint)#Operation not supported."); } } }