/***********************************************************************
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<wchar_t>& 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<WString>& 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<WString>& 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<unsigned char> 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<WString>& 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<WString>& 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<Folder> folders;
				GetFolders(folders);
				for (auto folder : folders)
				{
					if (!folder.Delete(true)) return false;
				}
				
				List<File> 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<IFileSystemImpl>& GetFileSystemInjection()
		{
			static feature_injection::FeatureInjection<IFileSystemImpl> 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<Folder>& folders) const
		{
			return GetFileSystemImpl()->GetFolders(filePath, folders);
		}

		bool Folder::GetFiles(collections::List<File>& 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<Locale>& locales) const
	{
		locales.Add(Locale(L"en-US"));
	}

	void EnUsLocaleImpl::GetShortDateFormats(const WString&, List<WString>& formats) const
	{
		formats.Add(L"MM/dd/yyyy");
		formats.Add(L"yyyy-MM-dd");
	}

	void EnUsLocaleImpl::GetLongDateFormats(const WString&, List<WString>& formats) const
	{
		formats.Add(L"dddd, dd MMMM yyyy");
	}

	void EnUsLocaleImpl::GetYearMonthDateFormats(const WString&, List<WString>& formats) const
	{
		formats.Add(L"yyyy MMMM");
	}

	void EnUsLocaleImpl::GetLongTimeFormats(const WString&, List<WString>& formats) const
	{
		formats.Add(L"HH:mm:ss");
	}

	void EnUsLocaleImpl::GetShortTimeFormats(const WString&, List<WString>& 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<vint, vint> EnUsLocaleImpl::FindFirst(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const
	{
		if (text.Length() < find.Length() || find.Length() == 0)
		{
			return Pair<vint, vint>(-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<vint, vint>(-1, 0) : Pair<vint, vint>(result - text.Buffer(), find.Length());
	}

	Pair<vint, vint> EnUsLocaleImpl::FindLast(const WString&, const WString& text, const WString& find, Locale::Normalization normalization) const
	{
		if (text.Length() < find.Length() || find.Length() == 0)
		{
			return Pair<vint, vint>(-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<vint, vint>(-1, 0) : Pair<vint, vint>(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<ILocaleImpl>& GetLocaleInjection()
	{
		static feature_injection::FeatureInjection<ILocaleImpl> 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<Locale>& locales)
	{
		GetLocaleInjection().Get()->Enumerate(locales);
	}

/***********************************************************************
Locale (ILocaleImpl redirections)
***********************************************************************/

	void Locale::GetShortDateFormats(collections::List<WString>& formats)const
	{
		GetLocaleInjection().Get()->GetShortDateFormats(localeName, formats);
	}

	void Locale::GetLongDateFormats(collections::List<WString>& formats)const
	{
		GetLocaleInjection().Get()->GetLongDateFormats(localeName, formats);
	}

	void Locale::GetYearMonthDateFormats(collections::List<WString>& formats)const
	{
		GetLocaleInjection().Get()->GetYearMonthDateFormats(localeName, formats);
	}

	void Locale::GetLongTimeFormats(collections::List<WString>& formats)const
	{
		GetLocaleInjection().Get()->GetLongTimeFormats(localeName, formats);
	}

	void Locale::GetShortTimeFormats(collections::List<WString>& 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<vint, vint> Locale::FindFirst(const WString& text, const WString& find, Normalization normalization)const
	{
		return GetLocaleInjection().Get()->FindFirst(localeName, text, find, normalization);
	}

	collections::Pair<vint, vint> 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 <arm_acle.h>
#elif defined VCZH_MSVC || defined VCZH_GCC
#include <emmintrin.h>
#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<char> 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<char> 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(bomPosition<bomLength)
			{
				vint remain=bomLength-bomPosition;
				result=remain<_size?remain:_size;
				memcpy(buffer, bom+bomPosition, result);
				buffer+=result;
				bomPosition+=result;
				_size-=result;
			}
			if(_size)
			{
				result+=stream->Read(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<size;i++)
			{
				if(buffer[i]==0) return false;
			}
			return true;
		}

		bool CanBeUtf8(unsigned char* buffer, vint size)
		{
			for(vint i=0;i<size;i++)
			{
				unsigned char c=(unsigned char)buffer[i];
				if(c==0)
				{
					return false;
				}
				else
				{
					vint count10xxxxxx=0;
					if((c&0x80)==0x00) /* 0x0xxxxxxx */ count10xxxxxx=0;
					else if((c&0xE0)==0xC0) /* 0x110xxxxx */ count10xxxxxx=1;
					else if((c&0xF0)==0xE0) /* 0x1110xxxx */ count10xxxxxx=2;
					else if((c&0xF8)==0xF0) /* 0x11110xxx */ count10xxxxxx=3;
					else if((c&0xFC)==0xF8) /* 0x111110xx */ count10xxxxxx=4;
					else if((c&0xFE)==0xFC) /* 0x1111110x */ count10xxxxxx=5;

					if(size<=i+count10xxxxxx)
					{
						return false;
					}
					else
					{
						for(vint j=0;j<count10xxxxxx;j++)
						{
							c=(unsigned char)buffer[i+j+1];
							if((c&0xC0)!=0x80) /* 0x10xxxxxx */ return false;
						}
					}
					i+=count10xxxxxx;
				}
			}
			return true;
		}

		bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs)
		{
			hitSurrogatePairs = false;
			if (size % 2 != 0) return false;
			bool needTrail = false;
			for (vint i = 0; i < size; i += 2)
			{
				vuint16_t c = buffer[i] + (buffer[i + 1] << 8);
				if (c == 0) return false;
				vint type = 0;
				if (0xD800 <= c && c <= 0xDBFF) type = 1;
				else if (0xDC00 <= c && c <= 0xDFFF) type = 2;
				if (needTrail)
				{
					if (type == 2)
					{
						needTrail = false;
					}
					else
					{
						return false;
					}
				}
				else
				{
					if (type == 1)
					{
						needTrail = true;
						hitSurrogatePairs = true;
					}
					else if (type != 0)
					{
						return false;
					}
				}
			}
			return !needTrail;
		}

		bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs)
		{
			hitSurrogatePairs = false;
			if (size % 2 != 0) return false;
			bool needTrail = false;
			for (vint i = 0; i < size; i += 2)
			{
				vuint16_t c = buffer[i + 1] + (buffer[i] << 8);
				if (c == 0) return false;
				vint type = 0;
				if (0xD800 <= c && c <= 0xDBFF) type = 1;
				else if (0xDC00 <= c && c <= 0xDFFF) type = 2;
				if (needTrail)
				{
					if (type == 2)
					{
						needTrail = false;
					}
					else
					{
						return false;
					}
				}
				else
				{
					if (type == 1)
					{
						needTrail = true;
						hitSurrogatePairs = true;
					}
					else if (type != 0)
					{
						return false;
					}
				}
			}
			return !needTrail;
		}
		
/***********************************************************************
TestEncoding
***********************************************************************/

		extern void TestEncodingInternal(
			unsigned char* buffer,
			vint size,
			BomEncoder::Encoding& encoding,
			bool containsBom,
			bool utf16HitSurrogatePairs,
			bool utf16BEHitSurrogatePairs,
			bool roughMbcs,
			bool roughUtf8,
			bool roughUtf16,
			bool roughUtf16BE
		);

		void TestEncoding(unsigned char* buffer, vint size, BomEncoder::Encoding& encoding, bool& containsBom)
		{
			if (size >= 3 && strncmp((char*)buffer, "\xEF\xBB\xBF", 3) == 0)
			{
				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<typename TNative, typename TExpect>
		vint UtfGeneralEncoder<TNative, TExpect>::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<T>::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<T>::Write(void*, vint)#Unwritten text is too large to cache.");
				memcpy(cacheBuffer, unicode + availableBytes, cacheSize);
			}

			if (needToFree) delete[] unicode;
			return _size;
		}

/***********************************************************************
UtfGeneralDecoder
***********************************************************************/

		template<typename TNative, typename TExpect>
		void UtfGeneralDecoder<TNative, TExpect>::Setup(IStream* _stream)
		{
			DecoderBase::Setup(_stream);
			reader.Setup(_stream);
		}

		template<typename TNative, typename TExpect>
		vint UtfGeneralDecoder<TNative, TExpect>::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<T, T>
***********************************************************************/

		template<typename T>
		vint UtfGeneralEncoder<T, T>::Write(void* _buffer, vint _size)
		{
			return stream->Write(_buffer, _size);
		}

/***********************************************************************
UtfGeneralDecoder<T, T>
***********************************************************************/

		template<typename T>
		vint UtfGeneralDecoder<T, T>::Read(void* _buffer, vint _size)
		{
			return stream->Read(_buffer, _size);
		}

/***********************************************************************
Unicode General (extern templates)
***********************************************************************/

		template class UtfGeneralEncoder<wchar_t, wchar_t>;
		template class UtfGeneralEncoder<wchar_t, char8_t>;
		template class UtfGeneralEncoder<wchar_t, char16_t>;
		template class UtfGeneralEncoder<wchar_t, char16be_t>;
		template class UtfGeneralEncoder<wchar_t, char32_t>;

		template class UtfGeneralEncoder<char8_t, wchar_t>;
		template class UtfGeneralEncoder<char8_t, char8_t>;
		template class UtfGeneralEncoder<char8_t, char16_t>;
		template class UtfGeneralEncoder<char8_t, char16be_t>;
		template class UtfGeneralEncoder<char8_t, char32_t>;

		template class UtfGeneralEncoder<char16_t, wchar_t>;
		template class UtfGeneralEncoder<char16_t, char8_t>;
		template class UtfGeneralEncoder<char16_t, char16_t>;
		template class UtfGeneralEncoder<char16_t, char16be_t>;
		template class UtfGeneralEncoder<char16_t, char32_t>;

		template class UtfGeneralEncoder<char16be_t, wchar_t>;
		template class UtfGeneralEncoder<char16be_t, char8_t>;
		template class UtfGeneralEncoder<char16be_t, char16_t>;
		template class UtfGeneralEncoder<char16be_t, char16be_t>;
		template class UtfGeneralEncoder<char16be_t, char32_t>;

		template class UtfGeneralEncoder<char32_t, wchar_t>;
		template class UtfGeneralEncoder<char32_t, char8_t>;
		template class UtfGeneralEncoder<char32_t, char16_t>;
		template class UtfGeneralEncoder<char32_t, char16be_t>;
		template class UtfGeneralEncoder<char32_t, char32_t>;

		template class UtfGeneralDecoder<wchar_t, wchar_t>;
		template class UtfGeneralDecoder<wchar_t, char8_t>;
		template class UtfGeneralDecoder<wchar_t, char16_t>;
		template class UtfGeneralDecoder<wchar_t, char16be_t>;
		template class UtfGeneralDecoder<wchar_t, char32_t>;

		template class UtfGeneralDecoder<char8_t, wchar_t>;
		template class UtfGeneralDecoder<char8_t, char8_t>;
		template class UtfGeneralDecoder<char8_t, char16_t>;
		template class UtfGeneralDecoder<char8_t, char16be_t>;
		template class UtfGeneralDecoder<char8_t, char32_t>;

		template class UtfGeneralDecoder<char16_t, wchar_t>;
		template class UtfGeneralDecoder<char16_t, char8_t>;
		template class UtfGeneralDecoder<char16_t, char16_t>;
		template class UtfGeneralDecoder<char16_t, char16be_t>;
		template class UtfGeneralDecoder<char16_t, char32_t>;

		template class UtfGeneralDecoder<char16be_t, wchar_t>;
		template class UtfGeneralDecoder<char16be_t, char8_t>;
		template class UtfGeneralDecoder<char16be_t, char16_t>;
		template class UtfGeneralDecoder<char16be_t, char16be_t>;
		template class UtfGeneralDecoder<char16be_t, char32_t>;

		template class UtfGeneralDecoder<char32_t, wchar_t>;
		template class UtfGeneralDecoder<char32_t, char8_t>;
		template class UtfGeneralDecoder<char32_t, char16_t>;
		template class UtfGeneralDecoder<char32_t, char16be_t>;
		template class UtfGeneralDecoder<char32_t, char32_t>;
	}
}


/***********************************************************************
.\STREAM\ACCESSOR.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <string.h>

namespace vl
{
	namespace stream
	{
		using namespace collections;

		template<typename T>
		struct VCRLF_ {};

		template<> struct VCRLF_<wchar_t> { static constexpr const wchar_t* Value = L"\r\n"; };
		template<> struct VCRLF_<char8_t> { static constexpr const char8_t* Value = u8"\r\n"; };
		template<> struct VCRLF_<char16_t> { static constexpr const char16_t* Value = u"\r\n"; };
		template<> struct VCRLF_<char32_t> { static constexpr const char32_t* Value = U"\r\n"; };

		template<typename T>
		constexpr const T* VCRLF = VCRLF_<T>::Value;

		template<typename T>
		struct VEMPTYSTR_ {};

		template<> struct VEMPTYSTR_<wchar_t> { static constexpr const wchar_t* Value = L""; };
		template<> struct VEMPTYSTR_<char8_t> { static constexpr const char8_t* Value = u8""; };
		template<> struct VEMPTYSTR_<char16_t> { static constexpr const char16_t* Value = u""; };
		template<> struct VEMPTYSTR_<char32_t> { static constexpr const char32_t* Value = U""; };

		template<typename T>
		constexpr const T* VEMPTYSTR = VEMPTYSTR_<T>::Value;

/***********************************************************************
TextReader_<T>
***********************************************************************/

		template<typename T>
		ObjectString<T> TextReader_<T>::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<T> result(buffer);
			delete[] buffer;
			return result;
		}

		template<typename T>
		ObjectString<T> TextReader_<T>::ReadLine()
		{
			ObjectString<T> 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<typename T>
		ObjectString<T> TextReader_<T>::ReadToEnd()
		{
			ObjectString<T> 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_<T>
***********************************************************************/

		template<typename T>
		void TextWriter_<T>::WriteString(const T* string, vint charCount)
		{
			while (*string)
			{
				WriteChar(*string++);
			}
		}

		template<typename T>
		void TextWriter_<T>::WriteString(const T* string)
		{
			vint len = 0;
			if constexpr (std::is_same_v<T, char>)
			{
				len = strlen(string);
			}
			else if constexpr (std::is_same_v<T, char8_t>)
			{
				len = strlen((const char*)string);
			}
			else if constexpr (std::is_same_v<T, wchar_t>)
			{
				len = wcslen(string);
			}
#if defined VCZH_WCHAR_UTF16
			else if constexpr (std::is_same_v<T, char16_t>)
			{
				len = wcslen((const wchar_t*)string);
			}
#elif defined VCZH_WCHAR_UTF32
			else if constexpr (std::is_same_v<T, char32_t>)
			{
				len = wcslen((const wchar_t*)string);
			}
#endif
			else
			{
				len = ObjectString<T>::Unmanaged(string).Length();
			}
			WriteString(string, len);
		}

		template<typename T>
		void TextWriter_<T>::WriteString(const ObjectString<T>& string)
		{
			if (string.Length())
			{
				WriteString(string.Buffer(), string.Length());
			}
		}

		template<typename T>
		void TextWriter_<T>::WriteLine(const T* string, vint charCount)
		{
			WriteString(string, charCount);
			WriteString(VCRLF<T>, 2);
		}

		template<typename T>
		void TextWriter_<T>::WriteLine(const T* string)
		{
			WriteString(string);
			WriteString(VCRLF<T>, 2);
		}

		template<typename T>
		void TextWriter_<T>::WriteLine(const ObjectString<T>& string)
		{
			WriteString(string);
			WriteString(VCRLF<T>, 2);
		}

/***********************************************************************
StringReader_<T>
***********************************************************************/

		template<typename T>
		void StringReader_<T>::PrepareIfLastCallIsReadLine()
		{
			if (lastCallIsReadLine)
			{
				lastCallIsReadLine = false;
				if (current < string.Length() && string[current] == L'\r') current++;
				if (current < string.Length() && string[current] == L'\n') current++;
			}
		}

		template<typename T>
		StringReader_<T>::StringReader_(const ObjectString<T>& _string)
			: string(_string)
			, current(0)
			, lastCallIsReadLine(false)
		{
		}

		template<typename T>
		bool StringReader_<T>::IsEnd()
		{
			return current == string.Length();
		}

		template<typename T>
		T StringReader_<T>::ReadChar()
		{
			PrepareIfLastCallIsReadLine();
			if (IsEnd())
			{
				return 0;
			}
			else
			{
				return string[current++];
			}
		}

		template<typename T>
		ObjectString<T> StringReader_<T>::ReadString(vint length)
		{
			PrepareIfLastCallIsReadLine();
			if (IsEnd())
			{
				return VEMPTYSTR<T>;
			}
			else
			{
				vint remain = string.Length() - current;
				if (length > remain) length = remain;
				ObjectString<T> result = string.Sub(current, length);
				current += length;
				return result;
			}
		}

		template<typename T>
		ObjectString<T> StringReader_<T>::ReadLine()
		{
			PrepareIfLastCallIsReadLine();
			if (IsEnd())
			{
				return VEMPTYSTR<T>;
			}
			else
			{
				vint lineEnd = current;
				while (lineEnd < string.Length())
				{
					T c = string[lineEnd];
					if (c == L'\r' || c == L'\n') break;
					lineEnd++;
				}
				ObjectString<T> result = string.Sub(current, lineEnd - current);
				current = lineEnd;
				lastCallIsReadLine = true;
				return result;
			}
		}

		template<typename T>
		ObjectString<T> StringReader_<T>::ReadToEnd()
		{
			return ReadString(string.Length() - current);
		}

/***********************************************************************
StreamReader_<T>
***********************************************************************/

		template<typename T>
		StreamReader_<T>::StreamReader_(IStream& _stream)
			: stream(&_stream)
		{
		}

		template<typename T>
		bool StreamReader_<T>::IsEnd()
		{
			return stream == nullptr;
		}

		template<typename T>
		T StreamReader_<T>::ReadChar()
		{
			if (stream)
			{
				T buffer = 0;
				if (stream->Read(&buffer, sizeof(buffer)) == 0)
				{
					stream = nullptr;
					return 0;
				}
				else
				{
					return buffer;
				}
			}
			else
			{
				return 0;
			}
		}

/***********************************************************************
StreamWriter_<T>
***********************************************************************/

		template<typename T>
		StreamWriter_<T>::StreamWriter_(IStream& _stream)
			:stream(&_stream)
		{
		}

		template<typename T>
		void StreamWriter_<T>::WriteChar(T c)
		{
			stream->Write(&c, sizeof(c));
		}

		template<typename T>
		void StreamWriter_<T>::WriteString(const T* string, vint charCount)
		{
			stream->Write((void*)string, charCount * sizeof(*string));
		}

/***********************************************************************
Extern Templates
***********************************************************************/

		template class TextReader_<wchar_t>;
		template class TextReader_<char8_t>;
		template class TextReader_<char16_t>;
		template class TextReader_<char32_t>;

		template class TextWriter_<wchar_t>;
		template class TextWriter_<char8_t>;
		template class TextWriter_<char16_t>;
		template class TextWriter_<char32_t>;

		template class StringReader_<wchar_t>;
		template class StringReader_<char8_t>;
		template class StringReader_<char16_t>;
		template class StringReader_<char32_t>;

		template class StreamReader_<wchar_t>;
		template class StreamReader_<char8_t>;
		template class StreamReader_<char16_t>;
		template class StreamReader_<char32_t>;

		template class StreamWriter_<wchar_t>;
		template class StreamWriter_<char8_t>;
		template class StreamWriter_<char16_t>;
		template class StreamWriter_<char32_t>;

/***********************************************************************
Extern Templates
***********************************************************************/

		namespace monospace_tabling
		{
			void WriteBorderLine(TextWriter& writer, Array<vint>& 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<vint>& columnWidths, vint rowHeight, vint columns, Array<WString>& 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<WString>& tableByRow, vint rows, vint columns)
		{
			Array<vint> rowHeights(rows);
			Array<vint> 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;i<streams.Count();i++)
			{
				vint written = streams[i]->Write(_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 && position<start+availableLength)
			{
				vint bufferMax=(vint)(start+availableLength-position);
				vint min=bufferMax<_size?bufferMax:_size;
				memcpy(_buffer, buffer+(position-start), min);
				readed+=min;
				_buffer=(char*)_buffer+min;
			}

			if(_size>readed)
			{
				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<remain?availableLength:remain;
					memcpy(_buffer, buffer, min);
					readed+=min;
				}
			}
			return readed;
		}

		vint CacheStream::InternalWrite(void* _buffer, vint _size)
		{
			vint written=0;
			if(position>=start && position<start+block)
			{
				vint bufferMax=(vint)(start+block-position);
				vint writeLength=bufferMax<_size?bufferMax:_size;
				vint writeStart=(vint)(position-start);

				memcpy(buffer+writeStart, _buffer, writeLength);
				written+=writeLength;
				_buffer=(char*)_buffer+writeLength;

				if(dirtyLength==0)
				{
					dirtyStart=writeStart;
					dirtyLength=writeLength;
				}
				else
				{
					dirtyLength=writeStart+writeLength-dirtyStart;
				}

				vint availableOffset=writeStart+writeLength-availableLength;
				if(availableOffset>0)
				{
					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<position)
			{
				operatedSize=position;
			}
			return _size;
		}

		vint CacheStream::Write(void* _buffer, vint _size)
		{
			CHECK_ERROR(CanWrite(), L"CacheStream::Write(void*, vint)#Stream is closed or operation not supported.");
			CHECK_ERROR(_size>=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<position)
			{
				operatedSize=position;
			}
			return _size;
		}

		vint CacheStream::Peek(void* _buffer, vint _size)
		{
			CHECK_ERROR(CanPeek(), L"CacheStream::Peek(void*, vint)#Stream is closed or operation not supported.");
			CHECK_ERROR(_size>=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<IFileStreamImpl> 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<position)
			{
				size=position;
			}
			return _size;
		}

		vint MemoryStream::Peek(void* _buffer, vint _size)
		{
			CHECK_ERROR(block!=0, L"MemoryStream::Peek(pos_t)#Stream is closed, cannot perform this operation.");
			CHECK_ERROR(_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.");
		}
	}
}
