/*********************************************************************** 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 ***********************************************************************/ namespace vl { namespace filesystem { using namespace collections; using namespace stream; // ReadDirectoryChangesW /*********************************************************************** FilePath ***********************************************************************/ void FilePath::NormalizeDelimiters(collections::Array& buffer) { for (vint i = 0; i < buffer.Count(); i++) { if (buffer[i] == L'\\' || buffer[i] == L'/') { buffer[i] = Delimiter; } } } void FilePath::TrimLastDelimiter(WString& fullPath) { if (fullPath != L"/" && fullPath.Length() > 0 && fullPath[fullPath.Length() - 1] == Delimiter) { fullPath = fullPath.Left(fullPath.Length() - 1); } } FilePath::FilePath() { Initialize(); } 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::operator/(const WString& relativePath)const { if (IsRoot()) { return relativePath; } else { return fullPath + L"/" + relativePath; } } WString FilePath::GetName()const { auto delimiter = WString::FromChar(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 { auto delimiter = WString::FromChar(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; } void FilePath::GetPathComponents(WString path, collections::List& components) { WString pathRemaining = path; auto delimiter = WString::FromChar(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_MSVC if (pathRemaining.Length() >= 2 && pathRemaining[1] == Delimiter) { // Windows UNC Path starting with "\\" // components[0] will be L"\\" components.Add(L"\\"); index.value++; } #elif defined VCZH_GCC // Unix absolute path starting with "/" // components[0] will be L"/" components.Add(delimiter); #endif } pathRemaining = pathRemaining.Right(pathRemaining.Length() - (index.key + index.value)); } if (pathRemaining.Length() != 0) { components.Add(pathRemaining); } } WString FilePath::ComponentsToPath(const collections::List& components) { WString result; auto delimiter = WString::FromChar(Delimiter); int i = 0; #if defined VCZH_MSVC // For Windows, if first component is "\\" then it is an UNC path if(components.Count() > 0 && components[0] == L"\\") { result += delimiter; i++; } #elif defined VCZH_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++; } #endif for(; i < components.Count(); i++) { result += components[i]; if(i + 1 < components.Count()) result += delimiter; } return result; } /*********************************************************************** File ***********************************************************************/ File::File(const FilePath& _filePath) :filePath(_filePath) { } 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); for (auto line : lines) { writer.WriteLine(line); } } delete encoder; return true; } bool File::Exists()const { return filePath.IsFile(); } /*********************************************************************** Folder ***********************************************************************/ Folder::Folder(const FilePath& _filePath) :filePath(_filePath) { } const FilePath& Folder::GetFilePath()const { return filePath; } 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 { return CreateNonRecursively(); } } bool Folder::Delete(bool recursively)const { if (!Exists()) return false; if (recursively) { List folders; GetFolders(folders); for (auto folder : folders) { if (!folder.Delete(true)) return false; } List files; GetFiles(files); for (auto file : files) { if (!file.Delete()) return false; } return Delete(false); } return DeleteNonRecursively(); } } } /*********************************************************************** .\FILESYSTEM.INJECTABLE.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace filesystem { extern IFileSystemImpl* GetOSFileSystemImpl(); feature_injection::FeatureInjection& GetFileSystemInjection() { static feature_injection::FeatureInjection injection(GetOSFileSystemImpl()); return injection; } void InjectFileSystemImpl(IFileSystemImpl* impl) { GetFileSystemInjection().Inject(impl); } IFileSystemImpl* GetFileSystemImpl() { return GetFileSystemInjection().Get(); } void EjectFileSystemImpl(IFileSystemImpl* impl) { if (impl == nullptr) { GetFileSystemInjection().EjectAll(); } else { GetFileSystemInjection().Eject(impl); } } /*********************************************************************** FilePath ***********************************************************************/ void FilePath::Initialize() { GetFileSystemImpl()->Initialize(fullPath); } bool FilePath::IsFile() const { return GetFileSystemImpl()->IsFile(fullPath); } bool FilePath::IsFolder() const { return GetFileSystemImpl()->IsFolder(fullPath); } bool FilePath::IsRoot() const { return GetFileSystemImpl()->IsRoot(fullPath); } WString FilePath::GetRelativePathFor(const FilePath& _filePath) const { return GetFileSystemImpl()->GetRelativePathFor(fullPath, _filePath.GetFullPath()); } /*********************************************************************** File ***********************************************************************/ bool File::Delete() const { return GetFileSystemImpl()->FileDelete(filePath); } bool File::Rename(const WString& newName) const { return GetFileSystemImpl()->FileRename(filePath, newName); } /*********************************************************************** Folder ***********************************************************************/ bool Folder::GetFolders(collections::List& folders) const { return GetFileSystemImpl()->GetFolders(filePath, folders); } bool Folder::GetFiles(collections::List& files) const { return GetFileSystemImpl()->GetFiles(filePath, files); } bool Folder::CreateNonRecursively() const { return GetFileSystemImpl()->CreateFolder(filePath); } bool Folder::DeleteNonRecursively() const { return GetFileSystemImpl()->DeleteFolder(filePath); } bool Folder::Rename(const WString& newName) const { return GetFileSystemImpl()->FolderRename(filePath, newName); } } } /*********************************************************************** .\LOCALE.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { using namespace collections; /*********************************************************************** EnUsLocaleImpl ***********************************************************************/ #ifdef VCZH_GCC #define _wcsicmp wcscasecmp #define _wcsnicmp wcsncasecmp #endif Locale EnUsLocaleImpl::Invariant() const { return Locale(L""); } Locale EnUsLocaleImpl::SystemDefault() const { return Locale(L"en-US"); } Locale EnUsLocaleImpl::UserDefault() const { return Locale(L"en-US"); } void EnUsLocaleImpl::Enumerate(List& locales) const { locales.Add(Locale(L"en-US")); } void EnUsLocaleImpl::GetShortDateFormats(const WString&, List& formats) const { formats.Add(L"MM/dd/yyyy"); formats.Add(L"yyyy-MM-dd"); } void EnUsLocaleImpl::GetLongDateFormats(const WString&, List& formats) const { formats.Add(L"dddd, dd MMMM yyyy"); } void EnUsLocaleImpl::GetYearMonthDateFormats(const WString&, List& formats) const { formats.Add(L"yyyy MMMM"); } void EnUsLocaleImpl::GetLongTimeFormats(const WString&, List& formats) const { formats.Add(L"HH:mm:ss"); } void EnUsLocaleImpl::GetShortTimeFormats(const WString&, List& formats) const { formats.Add(L"HH:mm"); formats.Add(L"hh:mm tt"); } WString EnUsLocaleImpl::FormatDate(const WString& localeName, const WString& format, DateTime date) const { /* auto df = L"yyyy,MM,MMM,MMMM,dd,ddd,dddd"; auto ds = L"2000,01,Jan,January,02,Sun,Sunday"; auto tf = L"hh,HH,mm,ss,tt"; auto ts = L"01,13,02,03,PM"; */ WString result; const wchar_t* reading = format.Buffer(); while (*reading) { if (wcsncmp(reading, L"yyyy", 4) == 0) { WString fragment = itow(date.year); while (fragment.Length() < 4) fragment = L"0" + fragment; result += fragment; reading += 4; } else if (wcsncmp(reading, L"MMMM", 4) == 0) { result += GetLongMonthName(localeName, date.month); reading += 4; } else if (wcsncmp(reading, L"MMM", 3) == 0) { result += GetShortMonthName(localeName, 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(localeName, date.dayOfWeek); reading += 4; } else if (wcsncmp(reading, L"ddd", 3) == 0) { result += GetShortDayOfWeekName(localeName, date.dayOfWeek); reading += 3; } else if (wcsncmp(reading, L"dd", 2) == 0) { WString fragment = itow(date.day); while (fragment.Length() < 2) fragment = L"0" + fragment; result += fragment; reading += 2; } else if (wcsncmp(reading, L"hh", 2) == 0) { WString fragment = itow(date.hour > 12 ? date.hour - 12 : date.hour); while (fragment.Length() < 2) fragment = L"0" + fragment; result += fragment; reading += 2; } else if (wcsncmp(reading, L"HH", 2) == 0) { WString fragment = itow(date.hour); while (fragment.Length() < 2) fragment = L"0" + fragment; result += fragment; reading += 2; } else if (wcsncmp(reading, L"mm", 2) == 0) { WString fragment = itow(date.minute); while (fragment.Length() < 2) fragment = L"0" + fragment; result += fragment; reading += 2; } else if (wcsncmp(reading, L"ss", 2) == 0) { WString fragment = itow(date.second); while (fragment.Length() < 2) fragment = L"0" + fragment; result += fragment; reading += 2; } else if (wcsncmp(reading, L"tt", 2) == 0) { result += date.hour > 12 ? L"PM" : L"AM"; reading += 2; } else { result += WString::FromChar(*reading); reading++; } } return result; } WString EnUsLocaleImpl::FormatTime(const WString& localeName, const WString& format, DateTime time) const { return FormatDate(localeName, format, time); } WString EnUsLocaleImpl::FormatNumber(const WString&, const WString& number) const { return number; } WString EnUsLocaleImpl::FormatCurrency(const WString&, const WString& currency) const { return currency; } WString EnUsLocaleImpl::GetShortDayOfWeekName(const WString&, vint dayOfWeek) const { switch (dayOfWeek) { case 0: return L"Sun"; case 1: return L"Mon"; case 2: return L"Tue"; case 3: return L"Wed"; case 4: return L"Thu"; case 5: return L"Fri"; case 6: return L"Sat"; } return L""; } WString EnUsLocaleImpl::GetLongDayOfWeekName(const WString&, vint dayOfWeek) const { switch (dayOfWeek) { case 0: return L"Sunday"; case 1: return L"Monday"; case 2: return L"Tuesday"; case 3: return L"Wednesday"; case 4: return L"Thursday"; case 5: return L"Friday"; case 6: return L"Saturday"; } return L""; } WString EnUsLocaleImpl::GetShortMonthName(const WString&, vint month) const { switch (month) { case 1: return L"Jan"; case 2: return L"Feb"; case 3: return L"Mar"; case 4: return L"Apr"; case 5: return L"May"; case 6: return L"Jun"; case 7: return L"Jul"; case 8: return L"Aug"; case 9: return L"Sep"; case 10: return L"Oct"; case 11: return L"Nov"; case 12: return L"Dec"; } return L""; } WString EnUsLocaleImpl::GetLongMonthName(const WString&, vint month) const { switch (month) { case 1: return L"January"; case 2: return L"February"; case 3: return L"March"; case 4: return L"April"; case 5: return L"May"; case 6: return L"June"; case 7: return L"July"; case 8: return L"August"; case 9: return L"September"; case 10: return L"October"; case 11: return L"November"; case 12: return L"December"; } return L""; } WString EnUsLocaleImpl::ToLower(const WString&, const WString& str) const { return wlower(str); } WString EnUsLocaleImpl::ToUpper(const WString&, const WString& str) const { return wupper(str); } WString EnUsLocaleImpl::ToLinguisticLower(const WString&, const WString& str) const { return wlower(str); } WString EnUsLocaleImpl::ToLinguisticUpper(const WString&, const WString& str) const { return wupper(str); } vint EnUsLocaleImpl::Compare(const WString&, const WString& s1, const WString& s2, Locale::Normalization normalization) const { switch (normalization) { case Locale::Normalization::None: return wcscmp(s1.Buffer(), s2.Buffer()); case Locale::Normalization::IgnoreCase: return _wcsicmp(s1.Buffer(), s2.Buffer()); default: return 0; } } vint EnUsLocaleImpl::CompareOrdinal(const WString& s1, const WString& s2) const { return wcscmp(s1.Buffer(), s2.Buffer()); } vint EnUsLocaleImpl::CompareOrdinalIgnoreCase(const WString& s1, const WString& s2) const { return _wcsicmp(s1.Buffer(), s2.Buffer()); } Pair EnUsLocaleImpl::FindFirst(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const { if (text.Length() < find.Length() || find.Length() == 0) { return Pair(-1, 0); } const wchar_t* result = nullptr; switch (normalization) { case Locale::Normalization::None: { const wchar_t* reading = text.Buffer(); while (*reading) { if (wcsncmp(reading, find.Buffer(), find.Length()) == 0) { result = reading; break; } reading++; } } break; case Locale::Normalization::IgnoreCase: { const wchar_t* reading = text.Buffer(); while (*reading) { if (_wcsnicmp(reading, find.Buffer(), find.Length()) == 0) { result = reading; break; } reading++; } } break; } return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); } Pair EnUsLocaleImpl::FindLast(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const { if (text.Length() < find.Length() || find.Length() == 0) { return Pair(-1, 0); } const wchar_t* result = nullptr; switch (normalization) { case Locale::Normalization::None: { const wchar_t* reading = text.Buffer(); while (*reading) { if (wcsncmp(reading, find.Buffer(), find.Length()) == 0) { result = reading; } reading++; } } break; case Locale::Normalization::IgnoreCase: { const wchar_t* reading = text.Buffer(); while (*reading) { if (_wcsnicmp(reading, find.Buffer(), find.Length()) == 0) { result = reading; } reading++; } } break; } return result == nullptr ? Pair(-1, 0) : Pair(result - text.Buffer(), find.Length()); } bool EnUsLocaleImpl::StartsWith(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const { if (text.Length() < find.Length() || find.Length() == 0) { return false; } switch (normalization) { case Locale::Normalization::None: return wcsncmp(text.Buffer(), find.Buffer(), find.Length()) == 0; case Locale::Normalization::IgnoreCase: return _wcsnicmp(text.Buffer(), find.Buffer(), find.Length()) == 0; } return false; } bool EnUsLocaleImpl::EndsWith(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const { if (text.Length() < find.Length() || find.Length() == 0) { return false; } switch (normalization) { case Locale::Normalization::None: return wcsncmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; case Locale::Normalization::IgnoreCase: return _wcsnicmp(text.Buffer() + text.Length() - find.Length(), find.Buffer(), find.Length()) == 0; } return false; } #ifdef VCZH_GCC #undef _wcsicmp #undef _wcsnicmp #endif /*********************************************************************** InjectLocaleImpl ***********************************************************************/ extern ILocaleImpl* GetOSLocaleImpl(); feature_injection::FeatureInjection& GetLocaleInjection() { static feature_injection::FeatureInjection injection(GetOSLocaleImpl()); return injection; } void InjectLocaleImpl(ILocaleImpl* impl) { GetLocaleInjection().Inject(impl); } void EjectLocaleImpl(ILocaleImpl* impl) { if (impl == nullptr) { GetLocaleInjection().EjectAll(); } else { GetLocaleInjection().Eject(impl); } } /*********************************************************************** Locale ***********************************************************************/ Locale::Locale(const WString& _localeName) :localeName(_localeName) { } const WString& Locale::GetName()const { return localeName; } /*********************************************************************** Locale (static) ***********************************************************************/ Locale Locale::Invariant() { return GetLocaleInjection().Get()->Invariant(); } Locale Locale::SystemDefault() { return GetLocaleInjection().Get()->SystemDefault(); } Locale Locale::UserDefault() { return GetLocaleInjection().Get()->UserDefault(); } void Locale::Enumerate(collections::List& locales) { GetLocaleInjection().Get()->Enumerate(locales); } /*********************************************************************** Locale (ILocaleImpl redirections) ***********************************************************************/ void Locale::GetShortDateFormats(collections::List& formats)const { GetLocaleInjection().Get()->GetShortDateFormats(localeName, formats); } void Locale::GetLongDateFormats(collections::List& formats)const { GetLocaleInjection().Get()->GetLongDateFormats(localeName, formats); } void Locale::GetYearMonthDateFormats(collections::List& formats)const { GetLocaleInjection().Get()->GetYearMonthDateFormats(localeName, formats); } void Locale::GetLongTimeFormats(collections::List& formats)const { GetLocaleInjection().Get()->GetLongTimeFormats(localeName, formats); } void Locale::GetShortTimeFormats(collections::List& formats)const { GetLocaleInjection().Get()->GetShortTimeFormats(localeName, formats); } WString Locale::FormatDate(const WString& format, DateTime date)const { return GetLocaleInjection().Get()->FormatDate(localeName, format, date); } WString Locale::FormatTime(const WString& format, DateTime time)const { return GetLocaleInjection().Get()->FormatTime(localeName, format, time); } WString Locale::FormatNumber(const WString& number)const { return GetLocaleInjection().Get()->FormatNumber(localeName, number); } WString Locale::FormatCurrency(const WString& currency)const { return GetLocaleInjection().Get()->FormatCurrency(localeName, currency); } WString Locale::GetShortDayOfWeekName(vint dayOfWeek)const { return GetLocaleInjection().Get()->GetShortDayOfWeekName(localeName, dayOfWeek); } WString Locale::GetLongDayOfWeekName(vint dayOfWeek)const { return GetLocaleInjection().Get()->GetLongDayOfWeekName(localeName, dayOfWeek); } WString Locale::GetShortMonthName(vint month)const { return GetLocaleInjection().Get()->GetShortMonthName(localeName, month); } WString Locale::GetLongMonthName(vint month)const { return GetLocaleInjection().Get()->GetLongMonthName(localeName, month); } WString Locale::ToLower(const WString& str)const { return GetLocaleInjection().Get()->ToLower(localeName, str); } WString Locale::ToUpper(const WString& str)const { return GetLocaleInjection().Get()->ToUpper(localeName, str); } WString Locale::ToLinguisticLower(const WString& str)const { return GetLocaleInjection().Get()->ToLinguisticLower(localeName, str); } WString Locale::ToLinguisticUpper(const WString& str)const { return GetLocaleInjection().Get()->ToLinguisticUpper(localeName, str); } vint Locale::Compare(const WString& s1, const WString& s2, Normalization normalization)const { return GetLocaleInjection().Get()->Compare(localeName, s1, s2, normalization); } vint Locale::CompareOrdinal(const WString& s1, const WString& s2)const { return GetLocaleInjection().Get()->CompareOrdinal(s1, s2); } vint Locale::CompareOrdinalIgnoreCase(const WString& s1, const WString& s2)const { return GetLocaleInjection().Get()->CompareOrdinalIgnoreCase(s1, s2); } collections::Pair Locale::FindFirst(const WString& text, const WString& find, Normalization normalization)const { return GetLocaleInjection().Get()->FindFirst(localeName, text, find, normalization); } collections::Pair Locale::FindLast(const WString& text, const WString& find, Normalization normalization)const { return GetLocaleInjection().Get()->FindLast(localeName, text, find, normalization); } bool Locale::StartsWith(const WString& text, const WString& find, Normalization normalization)const { return GetLocaleInjection().Get()->StartsWith(localeName, text, find, normalization); } bool Locale::EndsWith(const WString& text, const WString& find, Normalization normalization)const { return GetLocaleInjection().Get()->EndsWith(localeName, text, find, normalization); } } /*********************************************************************** .\THREADING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #if defined VCZH_ARM #include #elif defined VCZH_MSVC || defined VCZH_GCC #include #endif namespace vl { /*********************************************************************** SpinLock ***********************************************************************/ SpinLock::Scope::Scope(SpinLock& _spinLock) :spinLock(&_spinLock) { spinLock->Enter(); } SpinLock::Scope::~Scope() { spinLock->Leave(); } bool SpinLock::TryEnter() { return token.exchange(1) == 0; } void SpinLock::Enter() { vint expected = 0; while (!token.compare_exchange_strong(expected, 1)) { while (token != 0) { #ifdef VCZH_ARM __yield(); #else _mm_pause(); #endif } } } void SpinLock::Leave() { token.exchange(0); } /*********************************************************************** ThreadLocalStorage ***********************************************************************/ 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; } } } /*********************************************************************** .\ENCODING\BASE64ENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { const char8_t Utf8Base64Codes[] = u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /*********************************************************************** Utf8Base64Encoder ***********************************************************************/ void Utf8Base64Encoder::WriteBytesToCharArray(uint8_t* fromBytes, char8_t(&toChars)[Base64CycleChars], vint bytes) { switch (bytes) { case 1: { toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; toChars[1] = Utf8Base64Codes[(fromBytes[0] % (1 << 2)) << 4]; toChars[2] = u8'='; toChars[3] = u8'='; } break; case 2: { toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; toChars[1] = Utf8Base64Codes[((fromBytes[0] % (1 << 2)) << 4) | (fromBytes[1] >> 4)]; toChars[2] = Utf8Base64Codes[(fromBytes[1] % (1 << 4)) << 2]; toChars[3] = u8'='; } break; case 3: { toChars[0] = Utf8Base64Codes[fromBytes[0] >> 2]; toChars[1] = Utf8Base64Codes[((fromBytes[0] % (1 << 2)) << 4) | (fromBytes[1] >> 4)]; toChars[2] = Utf8Base64Codes[((fromBytes[1] % (1 << 4)) << 2) | (fromBytes[2] >> 6)]; toChars[3] = Utf8Base64Codes[fromBytes[2] % (1 << 6)]; } break; default: CHECK_FAIL(L"vl::stream::Utf8Base64Encoder::WriteBytesToCharArray(uint8_t*, char8_t(&)[Base64CycleChars], vint)#Parameter bytes should be 1, 2 or 3."); } } bool Utf8Base64Encoder::WriteCycle(uint8_t*& reading, vint& _size) { if (_size <= 0) return false; vint bytes = _size < Base64CycleBytes ? _size : Base64CycleBytes; char8_t chars[Base64CycleChars]; WriteBytesToCharArray(reading, chars, bytes); vint writtenBytes = stream->Write(chars, Base64CycleChars); CHECK_ERROR(writtenBytes == Base64CycleChars, L"vl::stream::Utf8Base64Encoder::WriteCycle(uint8_t*&, vint&)#The underlying stream failed to accept enough base64 characters."); reading += bytes; _size -= bytes; return true; } bool Utf8Base64Encoder::WriteCache(uint8_t*& reading, vint& _size) { if (cacheSize > 0 || _size < Base64CycleBytes) { vint copiedBytes = Base64CycleBytes - cacheSize; if (copiedBytes > _size) copiedBytes = _size; if (copiedBytes > 0) { memcpy(cache + cacheSize, reading, copiedBytes); reading += copiedBytes; _size -= copiedBytes; cacheSize += copiedBytes; } } if (cacheSize == 0) return _size > 0; if (cacheSize == Base64CycleBytes) { uint8_t* cacheReading = cache; return WriteCycle(cacheReading, cacheSize); } return true; } vint Utf8Base64Encoder::Write(void* _buffer, vint _size) { uint8_t* reading = (uint8_t*)_buffer; // flush cache if any if (!WriteCache(reading, _size)) goto FINISHED_WRITING; // run Base64 encoding while (_size >= Base64CycleBytes) { if (!WriteCycle(reading, _size)) goto FINISHED_WRITING; } // run the last Base64 encoding cycle and wrote a postfix to cache WriteCache(reading, _size); FINISHED_WRITING: return reading - (uint8_t*)_buffer; } void Utf8Base64Encoder::Close() { if (cacheSize > 0) { char8_t chars[Base64CycleChars]; WriteBytesToCharArray(cache, chars, cacheSize); vint writtenBytes = stream->Write(chars, Base64CycleChars); CHECK_ERROR(writtenBytes == Base64CycleChars, L"vl::stream::Utf8Base64Encoder::Close()#The underlying stream failed to accept enough base64 characters."); cacheSize = 0; } EncoderBase::Close(); } /*********************************************************************** Utf8Base64Decoder ***********************************************************************/ vint Utf8Base64Decoder::ReadBytesFromCharArray(char8_t(&fromChars)[Base64CycleChars], uint8_t* toBytes) { uint8_t nums[Base64CycleChars]; for (vint i = 0; i < Base64CycleChars; i++) { char8_t c = fromChars[i]; if (u8'A' <= c && c <= u8'Z') nums[i] = c - u8'A'; else if (u8'a' <= c && c <= u8'z') nums[i] = c - u8'a' + 26; else if (u8'0' <= c && c <= u8'9') nums[i] = c - u8'0' + 52; else switch (c) { case '+':nums[i] = 62; break; case '/':nums[i] = 63; break; case '=':nums[i] = 0; break; default: CHECK_FAIL(L"vl::stream::Utf8Base64Decoder::ReadBytesFromCharArray(char(&)[Base64CycleChars], uint8_t*)#Illegal Base64 character."); } } toBytes[0] = (nums[0] << 2) | (nums[1] >> 4); if (fromChars[2] == u8'=') return 1; toBytes[1] = ((nums[1] % (1 << 4)) << 4) | (nums[2] >> 2); if (fromChars[3] == u8'=') return 2; toBytes[2] = ((nums[2] % (1 << 2)) << 6) | nums[3]; return 3; } vint Utf8Base64Decoder::ReadCycle(uint8_t*& writing, vint& _size) { char8_t chars[Base64CycleChars]; vint readChars = stream->Read((void*)chars, Base64CycleChars); if (readChars == 0) return 0; CHECK_ERROR(readChars == Base64CycleChars, L"vl::stream::Utf8Base64Decoder::ReadCycle(uint8_t*&, vint&)#The underlying stream failed to provide enough base64 characters."); vint readBytes = ReadBytesFromCharArray(chars, writing); writing += readBytes; _size -= readBytes; return readBytes; } void Utf8Base64Decoder::ReadCache(uint8_t*& writing, vint& _size) { if (cacheSize > 0) { vint copiedBytes = cacheSize; if (copiedBytes > _size) copiedBytes = _size; if (copiedBytes > 0) { memcpy(writing, cache, copiedBytes); writing += copiedBytes; _size -= copiedBytes; cacheSize -= copiedBytes; if (cacheSize > 0) { memmove(cache, cache + copiedBytes, cacheSize); } } } } vint Utf8Base64Decoder::Read(void* _buffer, vint _size) { uint8_t* writing = (uint8_t*)_buffer; // write cache to buffer if any ReadCache(writing, _size); // run Base64 decoding while (_size >= Base64CycleBytes) { if (ReadCycle(writing, _size) == 0) goto FINISHED_READING; } // run the last Base64 decoding cycle and write a prefix to buffer if (_size > 0) { uint8_t* cacheWriting = cache; vint temp = 0; if ((cacheSize = ReadCycle(cacheWriting, temp)) == 0) goto FINISHED_READING; ReadCache(writing, _size); } FINISHED_READING: return writing - (uint8_t*)_buffer; } } } /*********************************************************************** .\ENCODING\ENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** EncoderBase ***********************************************************************/ void EncoderBase::Setup(IStream* _stream) { stream = _stream; } void EncoderBase::Close() { } /*********************************************************************** DecoderBase ***********************************************************************/ void DecoderBase::Setup(IStream* _stream) { stream = _stream; } void DecoderBase::Close() { } } } /*********************************************************************** .\ENCODING\LZWENCODING.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::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(); EncoderBase::Close(); } 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(nullptr); } } LzwDecoder::~LzwDecoder() { } 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"); } } } /*********************************************************************** .\ENCODING\CHARFORMAT\BOMENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** 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); } } } /*********************************************************************** .\ENCODING\CHARFORMAT\CHARFORMAT.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** Helper Functions ***********************************************************************/ bool CanBeMbcs(unsigned char* buffer, vint size) { for(vint i=0;i= 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) { TestEncodingInternal(buffer, size, encoding, containsBom, utf16HitSurrogatePairs, utf16BEHitSurrogatePairs, roughMbcs, roughUtf8, roughUtf16, roughUtf16BE); } } } } } /*********************************************************************** .\ENCODING\CHARFORMAT\MBCSENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** MbcsDecoder::ReadString ***********************************************************************/ extern bool IsMbcsLeadByte(char c); extern void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars); vint MbcsDecoder::ReadString(wchar_t* _buffer, vint chars) { char* source = new char[chars * 2]; char* reading = source; vint readed = 0; while (readed < chars) { if (stream->Read(reading, 1) != 1) { break; } if (IsMbcsLeadByte(*reading)) { if (stream->Read(reading + 1, 1) != 1) { break; } reading += 2; } else { reading++; } readed++; } MbcsToWChar(_buffer, chars, readed, source, (vint)(reading - source)); delete[] source; return readed; } /*********************************************************************** MbcsEncoder ***********************************************************************/ vint MbcsEncoder::Write(void* _buffer, vint _size) { // prepare a buffer for input vint availableChars = (cacheSize + _size) / sizeof(wchar_t); vint availableBytes = availableChars * sizeof(wchar_t); bool needToFree = false; vuint8_t* unicode = nullptr; if (cacheSize > 0) { unicode = new vuint8_t[cacheSize + _size]; memcpy(unicode, cacheBuffer, cacheSize); memcpy((vuint8_t*)unicode + cacheSize, _buffer, _size); needToFree = true; } else { unicode = (vuint8_t*)_buffer; } #if defined VCZH_WCHAR_UTF16 if (availableChars > 0) { // a surrogate pair must be written as a whole thing vuint16_t c = (vuint16_t)((wchar_t*)unicode)[availableChars - 1]; if ((c & 0xFC00U) == 0xD800U) { availableChars -= 1; availableBytes -= sizeof(wchar_t); } } #endif // write the buffer if (availableChars > 0) { vint written = WriteString((wchar_t*)unicode, availableChars) * sizeof(wchar_t); CHECK_ERROR(written == availableBytes, L"MbcsEncoder::Write(void*, vint)#Failed to write a complete string."); } // cache the remaining cacheSize = cacheSize + _size - availableBytes; if (cacheSize > 0) { CHECK_ERROR(cacheSize <= sizeof(char32_t), L"MbcsEncoder::Write(void*, vint)#Unwritten text is too large to cache."); memcpy(cacheBuffer, unicode + availableBytes, cacheSize); } if (needToFree) delete[] unicode; return _size; } /*********************************************************************** MbcsDecoder::WriteString ***********************************************************************/ // implemented in platform dependent files /*********************************************************************** MbcsDecoder ***********************************************************************/ vint MbcsDecoder::Read(void* _buffer, vint _size) { vuint8_t* writing = (vuint8_t*)_buffer; vint filledBytes = 0; // feed the cache first if (cacheSize > 0) { filledBytes = cacheSize < _size ? cacheSize : _size; memcpy(writing, cacheBuffer, cacheSize); _size -= filledBytes; writing += filledBytes; // adjust the cache if it is not fully consumed cacheSize -= filledBytes; if (cacheSize > 0) { memcpy(cacheBuffer, cacheBuffer + filledBytes, cacheSize); } if (_size == 0) { return filledBytes; } } // fill the buffer as many as possible while (_size >= sizeof(wchar_t)) { vint availableChars = _size / sizeof(wchar_t); vint readBytes = ReadString((wchar_t*)writing, availableChars) * sizeof(wchar_t); if (readBytes == 0) break; filledBytes += readBytes; _size -= readBytes; writing += readBytes; } // cache the remaining wchar_t if (_size < sizeof(wchar_t)) { wchar_t c; vint readChars = ReadString(&c, 1) * sizeof(wchar_t); if (readChars == sizeof(wchar_t)) { vuint8_t* reading = (vuint8_t*)&c; memcpy(writing, reading, _size); filledBytes += _size; cacheSize = sizeof(wchar_t) - _size; memcpy(cacheBuffer, reading + _size, cacheSize); } } return filledBytes; } } } /*********************************************************************** .\ENCODING\CHARFORMAT\UTFENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** UtfGeneralEncoder ***********************************************************************/ template vint UtfGeneralEncoder::Write(void* _buffer, vint _size) { // prepare a buffer for input vint availableChars = (cacheSize + _size) / sizeof(TExpect); vint availableBytes = availableChars * sizeof(TExpect); bool needToFree = false; vuint8_t* unicode = nullptr; if (cacheSize > 0) { unicode = new vuint8_t[cacheSize + _size]; memcpy(unicode, cacheBuffer, cacheSize); memcpy((vuint8_t*)unicode + cacheSize, _buffer, _size); needToFree = true; } else { unicode = (vuint8_t*)_buffer; } // write the buffer if (availableChars > 0) { TStringRangeReader reader((TExpect*)unicode, availableChars); while (TNative c = reader.Read()) { vint written = stream->Write(&c, sizeof(c)); if (written != sizeof(c)) { Close(); CHECK_FAIL(L"UtfGeneralEncoder::Write(void*, vint)#Failed to write a complete string."); } } auto cluster = reader.SourceCluster(); availableChars = cluster.index + cluster.size; availableBytes = availableChars * sizeof(TExpect); } // cache the remaining cacheSize = cacheSize + _size - availableBytes; if (cacheSize > 0) { CHECK_ERROR(cacheSize <= sizeof(cacheBuffer), L"UtfGeneralEncoder::Write(void*, vint)#Unwritten text is too large to cache."); memcpy(cacheBuffer, unicode + availableBytes, cacheSize); } if (needToFree) delete[] unicode; return _size; } /*********************************************************************** UtfGeneralDecoder ***********************************************************************/ template void UtfGeneralDecoder::Setup(IStream* _stream) { DecoderBase::Setup(_stream); reader.Setup(_stream); } template vint UtfGeneralDecoder::Read(void* _buffer, vint _size) { vuint8_t* writing = (vuint8_t*)_buffer; vint filledBytes = 0; // feed the cache first if (cacheSize > 0) { filledBytes = cacheSize < _size ? cacheSize : _size; memcpy(writing, cacheBuffer, cacheSize); _size -= filledBytes; writing += filledBytes; // adjust the cache if it is not fully consumed cacheSize -= filledBytes; if (cacheSize > 0) { memcpy(cacheBuffer, cacheBuffer + filledBytes, cacheSize); } if (_size == 0) { return filledBytes; } } // fill the buffer as many as possible while (_size >= sizeof(TExpect)) { vint availableChars = _size / sizeof(TExpect); vint readBytes = 0; for (vint i = 0; i < availableChars; i++) { TExpect c = reader.Read(); if (!c) break; *((TExpect*)writing) = c; writing += sizeof(TExpect); readBytes += sizeof(TExpect); } if (readBytes == 0) break; filledBytes += readBytes; _size -= readBytes; } // cache the remaining TExpect if (_size < sizeof(TExpect)) { if (TExpect c = reader.Read()) { vuint8_t* reading = (vuint8_t*)&c; memcpy(writing, reading, _size); filledBytes += _size; cacheSize = sizeof(TExpect) - _size; memcpy(cacheBuffer, reading + _size, cacheSize); } } return filledBytes; } /*********************************************************************** UtfGeneralEncoder ***********************************************************************/ template vint UtfGeneralEncoder::Write(void* _buffer, vint _size) { return stream->Write(_buffer, _size); } /*********************************************************************** UtfGeneralDecoder ***********************************************************************/ template vint UtfGeneralDecoder::Read(void* _buffer, vint _size) { return stream->Read(_buffer, _size); } /*********************************************************************** Unicode General (extern templates) ***********************************************************************/ template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralEncoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; template class UtfGeneralDecoder; } } /*********************************************************************** .\STREAM\ACCESSOR.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #include namespace vl { namespace stream { using namespace collections; template struct VCRLF_ {}; template<> struct VCRLF_ { static constexpr const wchar_t* Value = L"\r\n"; }; template<> struct VCRLF_ { static constexpr const char8_t* Value = u8"\r\n"; }; template<> struct VCRLF_ { static constexpr const char16_t* Value = u"\r\n"; }; template<> struct VCRLF_ { static constexpr const char32_t* Value = U"\r\n"; }; template constexpr const T* VCRLF = VCRLF_::Value; template struct VEMPTYSTR_ {}; template<> struct VEMPTYSTR_ { static constexpr const wchar_t* Value = L""; }; template<> struct VEMPTYSTR_ { static constexpr const char8_t* Value = u8""; }; template<> struct VEMPTYSTR_ { static constexpr const char16_t* Value = u""; }; template<> struct VEMPTYSTR_ { static constexpr const char32_t* Value = U""; }; template constexpr const T* VEMPTYSTR = VEMPTYSTR_::Value; /*********************************************************************** TextReader_ ***********************************************************************/ template ObjectString TextReader_::ReadString(vint length) { T* buffer = new T[length + 1]; vint i = 0; for (; i < length; i++) { if ((buffer[i] = ReadChar()) == 0) { break; } } buffer[i] = 0; ObjectString result(buffer); delete[] buffer; return result; } template ObjectString TextReader_::ReadLine() { ObjectString result; auto buffer = new T[65537]; buffer[0] = 0; vint i = 0; while (true) { T c = ReadChar(); if (c == L'\n' || c == 0) { buffer[i] = 0; result += buffer; buffer[0] = 0; i = 0; break; } else { if (i == 65536) { buffer[i] = 0; result += buffer; buffer[0] = 0; i = 0; } buffer[i++] = c; } } result += buffer; delete[] buffer; if (result.Length() > 0 && result[result.Length() - 1] == L'\r') { return result.Left(result.Length() - 1); } else { return result; } } template ObjectString TextReader_::ReadToEnd() { ObjectString result; auto buffer = new T[65537]; buffer[0] = 0; vint i = 0; while (true) { T c = ReadChar(); if (c == 0) { buffer[i] = 0; result += buffer; buffer[0] = 0; i = 0; break; } else { if (i == 65536) { buffer[i] = 0; result += buffer; buffer[0] = 0; i = 0; } buffer[i++] = c; } } result += buffer; delete[] buffer; return result; } /*********************************************************************** TextWriter_ ***********************************************************************/ template void TextWriter_::WriteString(const T* string, vint charCount) { while (*string) { WriteChar(*string++); } } template void TextWriter_::WriteString(const T* string) { vint len = 0; if constexpr (std::is_same_v) { len = strlen(string); } else if constexpr (std::is_same_v) { len = strlen((const char*)string); } else if constexpr (std::is_same_v) { len = wcslen(string); } #if defined VCZH_WCHAR_UTF16 else if constexpr (std::is_same_v) { len = wcslen((const wchar_t*)string); } #elif defined VCZH_WCHAR_UTF32 else if constexpr (std::is_same_v) { len = wcslen((const wchar_t*)string); } #endif else { len = ObjectString::Unmanaged(string).Length(); } WriteString(string, len); } template void TextWriter_::WriteString(const ObjectString& string) { if (string.Length()) { WriteString(string.Buffer(), string.Length()); } } template void TextWriter_::WriteLine(const T* string, vint charCount) { WriteString(string, charCount); WriteString(VCRLF, 2); } template void TextWriter_::WriteLine(const T* string) { WriteString(string); WriteString(VCRLF, 2); } template void TextWriter_::WriteLine(const ObjectString& string) { WriteString(string); WriteString(VCRLF, 2); } /*********************************************************************** StringReader_ ***********************************************************************/ template void StringReader_::PrepareIfLastCallIsReadLine() { if (lastCallIsReadLine) { lastCallIsReadLine = false; if (current < string.Length() && string[current] == L'\r') current++; if (current < string.Length() && string[current] == L'\n') current++; } } template StringReader_::StringReader_(const ObjectString& _string) : string(_string) , current(0) , lastCallIsReadLine(false) { } template bool StringReader_::IsEnd() { return current == string.Length(); } template T StringReader_::ReadChar() { PrepareIfLastCallIsReadLine(); if (IsEnd()) { return 0; } else { return string[current++]; } } template ObjectString StringReader_::ReadString(vint length) { PrepareIfLastCallIsReadLine(); if (IsEnd()) { return VEMPTYSTR; } else { vint remain = string.Length() - current; if (length > remain) length = remain; ObjectString result = string.Sub(current, length); current += length; return result; } } template ObjectString StringReader_::ReadLine() { PrepareIfLastCallIsReadLine(); if (IsEnd()) { return VEMPTYSTR; } else { vint lineEnd = current; while (lineEnd < string.Length()) { T c = string[lineEnd]; if (c == L'\r' || c == L'\n') break; lineEnd++; } ObjectString result = string.Sub(current, lineEnd - current); current = lineEnd; lastCallIsReadLine = true; return result; } } template ObjectString StringReader_::ReadToEnd() { return ReadString(string.Length() - current); } /*********************************************************************** StreamReader_ ***********************************************************************/ template StreamReader_::StreamReader_(IStream& _stream) : stream(&_stream) { } template bool StreamReader_::IsEnd() { return stream == nullptr; } template T StreamReader_::ReadChar() { if (stream) { T buffer = 0; if (stream->Read(&buffer, sizeof(buffer)) == 0) { stream = nullptr; return 0; } else { return buffer; } } else { return 0; } } /*********************************************************************** StreamWriter_ ***********************************************************************/ template StreamWriter_::StreamWriter_(IStream& _stream) :stream(&_stream) { } template void StreamWriter_::WriteChar(T c) { stream->Write(&c, sizeof(c)); } template void StreamWriter_::WriteString(const T* string, vint charCount) { stream->Write((void*)string, charCount * sizeof(*string)); } /*********************************************************************** Extern Templates ***********************************************************************/ template class TextReader_; template class TextReader_; template class TextReader_; template class TextReader_; template class TextWriter_; template class TextWriter_; template class TextWriter_; template class TextWriter_; template class StringReader_; template class StringReader_; template class StringReader_; template class StringReader_; template class StreamReader_; template class StreamReader_; template class StreamReader_; template class StreamReader_; template class StreamWriter_; template class StreamWriter_; template class StreamWriter_; template class StreamWriter_; /*********************************************************************** Extern Templates ***********************************************************************/ namespace monospace_tabling { void WriteBorderLine(TextWriter& writer, Array& columnWidths, vint columns) { writer.WriteChar(L'+'); for (vint i = 0; i < columns; i++) { vint c = columnWidths[i]; for (vint j = 0; j < c; j++) { writer.WriteChar(L'-'); } writer.WriteChar(L'+'); } writer.WriteLine(L""); } void WriteContentLine(TextWriter& writer, Array& columnWidths, vint rowHeight, vint columns, Array& tableByRow, vint startRow) { vint cellStart = startRow * columns; for (vint r = 0; r < rowHeight; r++) { writer.WriteChar(L'|'); for (vint c = 0; c < columns; c++) { const wchar_t* cell = tableByRow[cellStart + c].Buffer(); for (vint i = 0; i < r; i++) { if (cell) cell = ::wcsstr(cell, L"\r\n"); if (cell) cell += 2; } writer.WriteChar(L' '); vint length = 0; if (cell) { const wchar_t* end = ::wcsstr(cell, L"\r\n"); length = end ? end - cell : (vint)wcslen(cell); writer.WriteString(cell, length); } for (vint i = columnWidths[c] - 2; i >= length; i--) { writer.WriteChar(L' '); } writer.WriteChar(L'|'); } writer.WriteLine(L""); } } } void WriteMonospacedEnglishTable(TextWriter& writer, collections::Array& tableByRow, vint rows, vint columns) { Array rowHeights(rows); Array columnWidths(columns); for (vint i = 0; i < rows; i++) rowHeights[i] = 0; for (vint j = 0; j < columns; j++) columnWidths[j] = 0; for (vint i = 0; i < rows; i++) { for (vint j = 0; j < columns; j++) { WString text = tableByRow[i * columns + j]; const wchar_t* reading = text.Buffer(); vint width = 0; vint height = 0; while (reading) { height++; const wchar_t* crlf = ::wcsstr(reading, L"\r\n"); if (crlf) { vint length = crlf - reading + 2; if (width < length) width = length; reading = crlf + 2; } else { vint length = ::wcslen(reading) + 2; if (width < length) width = length; reading = 0; } } if (rowHeights[i] < height) rowHeights[i] = height; if (columnWidths[j] < width) columnWidths[j] = width; } } monospace_tabling::WriteBorderLine(writer, columnWidths, columns); for (vint i = 0; i < rows; i++) { monospace_tabling::WriteContentLine(writer, columnWidths, rowHeights[i], columns, tableByRow, i); monospace_tabling::WriteBorderLine(writer, columnWidths, columns); } } } } /*********************************************************************** .\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) { // TODO: (enumerable) foreach 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\ENCODINGSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** 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\FILESTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #if defined VCZH_GCC #endif namespace vl { namespace filesystem { extern IFileSystemImpl* GetFileSystemImpl(); } namespace stream { #if defined VCZH_GCC void _fseeki64(FILE* file, pos_t offset, int origin) { fseek(file, (long)offset, origin); } #endif /*********************************************************************** OSFileStreamImpl ***********************************************************************/ class OSFileStreamImpl : public Object, public virtual IFileStreamImpl { private: WString fileName; FileStream::AccessRight accessRight; FILE* file; public: OSFileStreamImpl(const WString& _fileName, FileStream::AccessRight _accessRight) : fileName(_fileName), accessRight(_accessRight), file(nullptr) { } ~OSFileStreamImpl() { Close(); } bool Open() override { const wchar_t* mode = L"rb"; switch(accessRight) { case FileStream::ReadOnly: mode = L"rb"; break; case FileStream::WriteOnly: mode = L"wb"; break; case FileStream::ReadWrite: mode = L"w+b"; break; } #if defined VCZH_MSVC if(_wfopen_s(&file, fileName.Buffer(), mode) != 0) { file = nullptr; return false; } #elif defined VCZH_GCC AString fileNameA = wtoa(fileName); AString modeA = wtoa(mode); file = fopen(fileNameA.Buffer(), modeA.Buffer()); if(file == nullptr) { return false; } #endif return true; } void Close() override { if(file != nullptr) { fclose(file); file = nullptr; } } pos_t Position() const override { if(file != nullptr) { #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 Size() const override { if(file != nullptr) { #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 Seek(pos_t _size) override { 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 SeekFromBegin(pos_t _size) override { if(_size > Size()) { _fseeki64(file, 0, SEEK_END); } else if(_size < 0) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, _size, SEEK_SET); } } void SeekFromEnd(pos_t _size) override { if(_size < 0) { _fseeki64(file, 0, SEEK_END); } else if(_size > Size()) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, -_size, SEEK_END); } } vint Read(void* _buffer, vint _size) override { CHECK_ERROR(file != nullptr, 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 Write(void* _buffer, vint _size) override { CHECK_ERROR(file != nullptr, 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 Peek(void* _buffer, vint _size) override { CHECK_ERROR(file != nullptr, 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 } }; /*********************************************************************** CreateOSFileStreamImpl ***********************************************************************/ Ptr CreateOSFileStreamImpl(const WString& fileName, FileStream::AccessRight accessRight) { return Ptr(new OSFileStreamImpl(fileName, accessRight)); } /*********************************************************************** FileStream ***********************************************************************/ FileStream::FileStream(const WString& fileName, AccessRight _accessRight) : accessRight(_accessRight) { impl = filesystem::GetFileSystemImpl()->GetFileStreamImpl(fileName, _accessRight); if(!impl->Open()) { impl = nullptr; } } FileStream::~FileStream() { Close(); } bool FileStream::CanRead() const { return impl != nullptr && (accessRight == ReadOnly || accessRight == ReadWrite); } bool FileStream::CanWrite() const { return impl != nullptr && (accessRight == WriteOnly || accessRight == ReadWrite); } bool FileStream::CanSeek() const { return impl != nullptr; } bool FileStream::CanPeek() const { return impl != nullptr && (accessRight == ReadOnly || accessRight == ReadWrite); } bool FileStream::IsLimited() const { return impl != nullptr && accessRight == ReadOnly; } bool FileStream::IsAvailable() const { return impl != nullptr; } void FileStream::Close() { if(impl != nullptr) { impl->Close(); impl = nullptr; } } pos_t FileStream::Position() const { return impl != nullptr ? impl->Position() : -1; } pos_t FileStream::Size() const { return impl != nullptr ? impl->Size() : -1; } void FileStream::Seek(pos_t _size) { CHECK_ERROR(impl != nullptr, L"FileStream::Seek(pos_t)#Stream is closed, cannot perform this operation."); impl->Seek(_size); } void FileStream::SeekFromBegin(pos_t _size) { CHECK_ERROR(impl != nullptr, L"FileStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); impl->SeekFromBegin(_size); } void FileStream::SeekFromEnd(pos_t _size) { CHECK_ERROR(impl != nullptr, L"FileStream::SeekFromEnd(pos_t)#Stream is closed, cannot perform this operation."); impl->SeekFromEnd(_size); } vint FileStream::Read(void* _buffer, vint _size) { CHECK_ERROR(impl != nullptr, L"FileStream::Read(pos_t)#Stream is closed, cannot perform this operation."); return impl->Read(_buffer, _size); } vint FileStream::Write(void* _buffer, vint _size) { CHECK_ERROR(impl != nullptr, L"FileStream::Write(pos_t)#Stream is closed, cannot perform this operation."); return impl->Write(_buffer, _size); } vint FileStream::Peek(void* _buffer, vint _size) { CHECK_ERROR(impl != nullptr, L"FileStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); return impl->Peek(_buffer, _size); } } } /*********************************************************************** .\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."); } } }