/*********************************************************************** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY DEVELOPER: Zihan Chen(vczh) ***********************************************************************/ #include "VlppOS.h" /*********************************************************************** .\FILESYSTEM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace filesystem { using namespace collections; using namespace stream; // ReadDirectoryChangesW /*********************************************************************** FilePath ***********************************************************************/ void FilePath::NormalizeDelimiters(collections::Array& buffer) { for (vint i = 0; i < buffer.Count(); i++) { if (buffer[i] == L'\\' || buffer[i] == L'/') { buffer[i] = Delimiter; } } } void FilePath::TrimLastDelimiter(WString& fullPath) { if (fullPath != L"/" && fullPath.Length() > 0 && fullPath[fullPath.Length() - 1] == Delimiter) { fullPath = fullPath.Left(fullPath.Length() - 1); } } FilePath::FilePath() { Initialize(); } FilePath::FilePath(const WString& _filePath) :fullPath(_filePath) { Initialize(); } FilePath::FilePath(const wchar_t* _filePath) :fullPath(_filePath) { Initialize(); } FilePath::FilePath(const FilePath& _filePath) :fullPath(_filePath.fullPath) { } FilePath FilePath::operator/(const WString& relativePath)const { if (IsRoot()) { return relativePath; } else { return fullPath + L"/" + relativePath; } } WString FilePath::GetName()const { auto delimiter = WString::FromChar(Delimiter); auto index = INVLOC.FindLast(fullPath, delimiter, Locale::None); if (index.key == -1) return fullPath; return fullPath.Right(fullPath.Length() - index.key - 1); } FilePath FilePath::GetFolder()const { auto delimiter = WString::FromChar(Delimiter); auto index = INVLOC.FindLast(fullPath, delimiter, Locale::None); if (index.key == -1) return FilePath(); return fullPath.Left(index.key); } WString FilePath::GetFullPath()const { return fullPath; } void FilePath::GetPathComponents(WString path, collections::List& components) { WString pathRemaining = path; auto delimiter = WString::FromChar(Delimiter); components.Clear(); while (true) { auto index = INVLOC.FindFirst(pathRemaining, delimiter, Locale::None); if (index.key == -1) break; if (index.key != 0) components.Add(pathRemaining.Left(index.key)); else { #if defined VCZH_MSVC if (pathRemaining.Length() >= 2 && pathRemaining[1] == Delimiter) { // Windows UNC Path starting with "\\" // components[0] will be L"\\" components.Add(L"\\"); index.value++; } #elif defined VCZH_GCC // Unix absolute path starting with "/" // components[0] will be L"/" components.Add(delimiter); #endif } pathRemaining = pathRemaining.Right(pathRemaining.Length() - (index.key + index.value)); } if (pathRemaining.Length() != 0) { components.Add(pathRemaining); } } WString FilePath::ComponentsToPath(const collections::List& components) { WString result; auto delimiter = WString::FromChar(Delimiter); int i = 0; #if defined VCZH_MSVC // For Windows, if first component is "\\" then it is an UNC path if(components.Count() > 0 && components[0] == L"\\") { result += delimiter; i++; } #elif defined VCZH_GCC // For Unix-like OSes, if first component is "/" then take it as absolute path if(components.Count() > 0 && components[0] == delimiter) { result += delimiter; i++; } #endif for(; i < components.Count(); i++) { result += components[i]; if(i + 1 < components.Count()) result += delimiter; } return result; } /*********************************************************************** File ***********************************************************************/ File::File(const FilePath& _filePath) :filePath(_filePath) { } const FilePath& File::GetFilePath()const { return filePath; } bool File::ReadAllTextWithEncodingTesting(WString& text, stream::BomEncoder::Encoding& encoding, bool& containsBom) { Array buffer; { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; if (fileStream.Size() == 0) { text = L""; encoding = BomEncoder::Mbcs; containsBom = false; return true; } buffer.Resize((vint)fileStream.Size()); vint count = fileStream.Read(&buffer[0], buffer.Count()); CHECK_ERROR(count == buffer.Count(), L"vl::filesystem::File::ReadAllTextWithEncodingTesting(WString&, BomEncoder::Encoding&, bool&)#Failed to read the whole file."); } TestEncoding(&buffer[0], buffer.Count(), encoding, containsBom); MemoryWrapperStream memoryStream(&buffer[0], buffer.Count()); if (containsBom) { BomDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } else { switch (encoding) { case BomEncoder::Utf8: { Utf8Decoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; case BomEncoder::Utf16: { Utf16Decoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; case BomEncoder::Utf16BE: { Utf16BEDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } break; default: { MbcsDecoder decoder; DecoderStream decoderStream(memoryStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); } } } return true; } WString File::ReadAllTextByBom()const { WString text; ReadAllTextByBom(text); return text; } bool File::ReadAllTextByBom(WString& text)const { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; BomDecoder decoder; DecoderStream decoderStream(fileStream, decoder); StreamReader reader(decoderStream); text = reader.ReadToEnd(); return true; } bool File::ReadAllLinesByBom(collections::List& lines)const { FileStream fileStream(filePath.GetFullPath(), FileStream::ReadOnly); if (!fileStream.IsAvailable()) return false; BomDecoder decoder; DecoderStream decoderStream(fileStream, decoder); StreamReader reader(decoderStream); while (!reader.IsEnd()) { lines.Add(reader.ReadLine()); } return true; } bool File::WriteAllText(const WString& text, bool bom, stream::BomEncoder::Encoding encoding) { FileStream fileStream(filePath.GetFullPath(), FileStream::WriteOnly); if (!fileStream.IsAvailable()) return false; IEncoder* encoder = nullptr; if (bom) { encoder = new BomEncoder(encoding); } else switch (encoding) { case BomEncoder::Utf8: encoder = new Utf8Encoder; break; case BomEncoder::Utf16: encoder = new Utf16Encoder; break; case BomEncoder::Utf16BE: encoder = new Utf16BEEncoder; break; default: encoder = new MbcsEncoder; break; } { EncoderStream encoderStream(fileStream, *encoder); StreamWriter writer(encoderStream); writer.WriteString(text); } delete encoder; return true; } bool File::WriteAllLines(collections::List& lines, bool bom, stream::BomEncoder::Encoding encoding) { FileStream fileStream(filePath.GetFullPath(), FileStream::WriteOnly); if (!fileStream.IsAvailable()) return false; IEncoder* encoder = nullptr; if (bom) { encoder = new BomEncoder(encoding); } else switch (encoding) { case BomEncoder::Utf8: encoder = new Utf8Encoder; break; case BomEncoder::Utf16: encoder = new Utf16Encoder; break; case BomEncoder::Utf16BE: encoder = new Utf16BEEncoder; break; default: encoder = new MbcsEncoder; break; } { EncoderStream encoderStream(fileStream, *encoder); StreamWriter writer(encoderStream); for (auto line : lines) { writer.WriteLine(line); } } delete encoder; return true; } bool File::Exists()const { return filePath.IsFile(); } /*********************************************************************** Folder ***********************************************************************/ Folder::Folder(const FilePath& _filePath) :filePath(_filePath) { } const FilePath& Folder::GetFilePath()const { return filePath; } bool Folder::Exists()const { return filePath.IsFolder(); } bool Folder::Create(bool recursively)const { if (recursively) { auto folder = filePath.GetFolder(); if (folder.IsFile()) return false; if (folder.IsFolder()) return Create(false); return Folder(folder).Create(true) && Create(false); } else { return CreateNonRecursively(); } } bool Folder::Delete(bool recursively)const { if (!Exists()) return false; if (recursively) { List folders; GetFolders(folders); for (auto folder : folders) { if (!folder.Delete(true)) return false; } List files; GetFiles(files); for (auto file : files) { if (!file.Delete()) return false; } return Delete(false); } return DeleteNonRecursively(); } } } /*********************************************************************** .\LOCALE.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { /*********************************************************************** Locale ***********************************************************************/ Locale::Locale(const WString& _localeName) :localeName(_localeName) { } const WString& Locale::GetName()const { return localeName; } } /*********************************************************************** .\THREADING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #if defined VCZH_ARM #elif defined VCZH_MSVC || defined VCZH_GCC #include #endif namespace vl { /*********************************************************************** SpinLock ***********************************************************************/ SpinLock::Scope::Scope(SpinLock& _spinLock) :spinLock(&_spinLock) { spinLock->Enter(); } SpinLock::Scope::~Scope() { spinLock->Leave(); } bool SpinLock::TryEnter() { return token.exchange(1) == 0; } void SpinLock::Enter() { vint expected = 0; while (!token.compare_exchange_strong(expected, 1)) { while (token != 0) { #ifdef VCZH_ARM __yield(); #else _mm_pause(); #endif } } } void SpinLock::Leave() { token.exchange(0); } /*********************************************************************** ThreadLocalStorage ***********************************************************************/ void ThreadLocalStorage::Clear() { CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Clear()#Cannot access a disposed ThreadLocalStorage."); if(destructor) { if (auto data = Get()) { destructor(data); } } Set(nullptr); } void ThreadLocalStorage::Dispose() { CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Dispose()#Cannot access a disposed ThreadLocalStorage."); Clear(); disposed = true; } struct TlsStorageLink { ThreadLocalStorage* storage = nullptr; TlsStorageLink* next = nullptr; }; volatile bool tlsFixed = false; TlsStorageLink* tlsHead = nullptr; TlsStorageLink** tlsTail = &tlsHead; void ThreadLocalStorage::PushStorage(ThreadLocalStorage* storage) { CHECK_ERROR(!tlsFixed, L"vl::ThreadLocalStorage::PushStorage(ThreadLocalStorage*)#Cannot create new ThreadLocalStorage instance after calling ThreadLocalStorage::FixStorages()."); auto link = new TlsStorageLink; link->storage = storage; *tlsTail = link; tlsTail = &link->next; } void ThreadLocalStorage::FixStorages() { tlsFixed = true; } void ThreadLocalStorage::ClearStorages() { FixStorages(); auto current = tlsHead; while (current) { current->storage->Clear(); current = current->next; } } void ThreadLocalStorage::DisposeStorages() { FixStorages(); auto current = tlsHead; tlsHead = nullptr; tlsTail = nullptr; while (current) { current->storage->Dispose(); auto temp = current; current = current->next; delete temp; } } } /*********************************************************************** .\STREAM\ACCESSOR.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ #include namespace vl { namespace stream { using namespace collections; /*********************************************************************** TextReader ***********************************************************************/ WString TextReader::ReadString(vint length) { wchar_t* buffer=new wchar_t[length+1]; vint i=0; for(;i0 && result[result.Length()-1]==L'\r') { return result.Left(result.Length()-1); } else { return result; } } WString TextReader::ReadToEnd() { WString result; auto buffer = new wchar_t[65537]; buffer[0]=L'\0'; vint i=0; while(true) { wchar_t c=ReadChar(); if(c==L'\0') { buffer[i]=L'\0'; result+=buffer; buffer[0]=L'\0'; i=0; break; } else { if(i==65536) { buffer[i]=L'\0'; result+=buffer; buffer[0]=L'\0'; i=0; } buffer[i++]=c; } } result+=buffer; delete[] buffer; return result; } /*********************************************************************** TextWriter ***********************************************************************/ void TextWriter::WriteString(const wchar_t* string, vint charCount) { while(*string) { WriteChar(*string++); } } void TextWriter::WriteString(const wchar_t* string) { WriteString(string, (vint)wcslen(string)); } void TextWriter::WriteString(const WString& string) { if(string.Length()) { WriteString(string.Buffer(), string.Length()); } } void TextWriter::WriteLine(const wchar_t* string, vint charCount) { WriteString(string, charCount); WriteString(L"\r\n", 2); } void TextWriter::WriteLine(const wchar_t* string) { WriteString(string); WriteString(L"\r\n", 2); } void TextWriter::WriteLine(const WString& string) { WriteString(string); WriteString(L"\r\n", 2); } namespace monospace_tabling { void WriteBorderLine(TextWriter& writer, Array& columnWidths, vint columns) { writer.WriteChar(L'+'); for(vint i=0;i& columnWidths, vint rowHeight, vint columns, Array& tableByRow, vint startRow) { vint cellStart=startRow*columns; for(vint r=0;r=length;i--) { writer.WriteChar(L' '); } writer.WriteChar(L'|'); } writer.WriteLine(L""); } } } using namespace monospace_tabling; void TextWriter::WriteMonospacedEnglishTable(collections::Array& tableByRow, vint rows, vint columns) { Array rowHeights(rows); Array columnWidths(columns); for(vint i=0;iremain) length=remain; WString result=string.Sub(current, length); current+=length; return result; } } WString StringReader::ReadLine() { PrepareIfLastCallIsReadLine(); if(IsEnd()) { return L""; } else { vint lineEnd=current; while(lineEndRead(&buffer, sizeof(buffer))==0) { stream=0; return 0; } else { return buffer; } } else { return L'\0'; } } /*********************************************************************** StreamWriter ***********************************************************************/ StreamWriter::StreamWriter(IStream& _stream) :stream(&_stream) { } void StreamWriter::WriteChar(wchar_t c) { stream->Write(&c, sizeof(c)); } void StreamWriter::WriteString(const wchar_t* string, vint charCount) { stream->Write((void*)string, charCount*sizeof(*string)); } } } /*********************************************************************** .\STREAM\BROADCASTSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** BroadcastStream ***********************************************************************/ BroadcastStream::BroadcastStream() :closed(false) ,position(0) { } BroadcastStream::~BroadcastStream() { } BroadcastStream::StreamList& BroadcastStream::Targets() { return streams; } bool BroadcastStream::CanRead()const { return false; } bool BroadcastStream::CanWrite()const { return !closed; } bool BroadcastStream::CanSeek()const { return false; } bool BroadcastStream::CanPeek()const { return false; } bool BroadcastStream::IsLimited()const { return false; } bool BroadcastStream::IsAvailable()const { return !closed; } void BroadcastStream::Close() { closed=true; position=-1; } pos_t BroadcastStream::Position()const { return position; } pos_t BroadcastStream::Size()const { return position; } void BroadcastStream::Seek(pos_t _size) { CHECK_FAIL(L"BroadcastStream::Seek(pos_t)#Operation not supported."); } void BroadcastStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"BroadcastStream::SeekFromBegin(pos_t)#Operation not supported."); } void BroadcastStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"BroadcastStream::SeekFromEnd(pos_t)#Operation not supported."); } vint BroadcastStream::Read(void* _buffer, vint _size) { CHECK_FAIL(L"BroadcastStream::Read(void*, vint)#Operation not supported."); } vint BroadcastStream::Write(void* _buffer, vint _size) { // TODO: (enumerable) foreach for(vint i=0;iWrite(_buffer, _size); CHECK_ERROR(written == _size, L"BroadcastStream::Write(void*, vint)#Failed to copy data to the output stream."); } position+=_size; return _size; } vint BroadcastStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"BroadcastStream::Peek(void*, vint)#Operation not supported."); } } } /*********************************************************************** .\STREAM\CACHESTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** CacheStream ***********************************************************************/ void CacheStream::Flush() { if(dirtyLength>0) { if(target->Position()!=start+dirtyStart) { target->SeekFromBegin(start+dirtyStart); } target->Write(buffer+dirtyStart, dirtyLength); } dirtyStart=0; dirtyLength=0; availableLength=0; } void CacheStream::Load(pos_t _position) { if(target->Position()!=_position) { target->SeekFromBegin(_position); } start=_position; if(target->CanRead()) { availableLength=target->Read(buffer, block); } } vint CacheStream::InternalRead(void* _buffer, vint _size) { vint readed=0; if(position>=start && positionreaded) { Flush(); if(_size-readed>=block) { if(CanSeek()) { target->SeekFromBegin(position+readed); } vint additional=target->Read(_buffer, _size-readed); if(additional!=-1) { readed+=additional; } } else { Load(position+readed); vint remain=_size-readed; vint min=availableLength=start && position0) { availableLength+=availableOffset; } } if(_size>written) { Flush(); if(_size-written>=block) { if(CanSeek()) { target->SeekFromBegin(position+written); } vint additional=target->Write(_buffer, _size-written); if(additional!=-1) { written+=additional; } } else { Load(position+written); dirtyLength=_size-written; memcpy(buffer, _buffer, dirtyLength); written+=dirtyLength; } } return written; } CacheStream::CacheStream(IStream& _target, vint _block) :target(&_target) ,block(_block) ,start(0) ,position(0) ,dirtyStart(0) ,dirtyLength(0) ,availableLength(0) ,operatedSize(0) { if(block<=0) { block=65536; } buffer=new char[block]; } CacheStream::~CacheStream() { Close(); } bool CacheStream::CanRead()const { return target!=0 && target->CanRead(); } bool CacheStream::CanWrite()const { return target!=0 && target->CanWrite(); } bool CacheStream::CanSeek()const { return target!=0 && target->CanSeek(); } bool CacheStream::CanPeek()const { return target!=0 && target->CanPeek(); } bool CacheStream::IsLimited()const { return target!=0 && target->IsLimited(); } bool CacheStream::IsAvailable()const { return target!=0 && target->IsAvailable(); } void CacheStream::Close() { Flush(); target=0; delete[] buffer; buffer=0; position=-1; dirtyStart=0; dirtyLength=0; availableLength=0; operatedSize=-1; } pos_t CacheStream::Position()const { return position; } pos_t CacheStream::Size()const { if(target!=0) { if(IsLimited()) { return target->Size(); } else { return operatedSize; } } else { return -1; } } void CacheStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void CacheStream::SeekFromBegin(pos_t _size) { if(CanSeek()) { if(_size<0) { position=0; } else if(_size>Size()) { position=Size(); } else { position=_size; } } } void CacheStream::SeekFromEnd(pos_t _size) { SeekFromBegin(Size()-_size); } vint CacheStream::Read(void* _buffer, vint _size) { CHECK_ERROR(CanRead(), L"CacheStream::Read(void*, vint)#Stream is closed or operation not supported."); CHECK_ERROR(_size>=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); _size=InternalRead(_buffer, _size); position+=_size; if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); if(IsLimited()) { pos_t size=Size(); if(size!=-1) { vint remain=(vint)(size-(position+_size)); if(remain<0) { _size-=remain; } } } _size=InternalWrite(_buffer, _size); position+=_size; if(operatedSize=0, L"CacheStream::Read(void*, vint)#Argument size cannot be negative."); return InternalRead(_buffer, _size); } } } /*********************************************************************** .\STREAM\CHARFORMAT.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** CharEncoder ***********************************************************************/ void CharEncoder::Setup(IStream* _stream) { stream = _stream; } void CharEncoder::Close() { } vint CharEncoder::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, needToFree) * sizeof(wchar_t); CHECK_ERROR(written == availableBytes, L"CharEncoder::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"CharEncoder::Write(void*, vint)#Unwritten text is too large to cache."); memcpy(cacheBuffer, unicode + availableBytes, cacheSize); } return _size; } /*********************************************************************** CharDecoder ***********************************************************************/ void CharDecoder::Setup(IStream* _stream) { stream=_stream; } void CharDecoder::Close() { } vint CharDecoder::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; } /*********************************************************************** Mbcs ***********************************************************************/ 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; } /*********************************************************************** Utf-16 ***********************************************************************/ vint Utf16Encoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) { #if defined VCZH_WCHAR_UTF16 vint size = chars * sizeof(wchar_t); vint written = stream->Write(_buffer, size); if (written != size) { Close(); return 0; } return chars; #elif defined VCZH_WCHAR_UTF32 WCharToUtfReader reader(_buffer, chars); while (char16_t c = reader.Read()) { vint written = stream->Write(&c, sizeof(c)); if (written != sizeof(c)) { Close(); return 0; } } if (reader.HasIllegalChar()) { Close(); return 0; } return chars; #endif } vint Utf16Decoder::ReadString(wchar_t* _buffer, vint chars) { #if defined VCZH_WCHAR_UTF16 vint read = stream->Read(_buffer, chars * sizeof(wchar_t)); CHECK_ERROR(read % sizeof(wchar_t) == 0, L"Utf16Decoder::ReadString(wchar_t*, vint)#Failed to read complete wchar_t characters."); return read / sizeof(wchar_t); #elif defined VCZH_WCHAR_UTF32 reader.Setup(stream); vint counter = 0; for (vint i = 0; i < chars; i++) { wchar_t c = reader.Read(); if (!c) break; _buffer[i] = c; counter++; } return counter; #endif } /*********************************************************************** Utf-16-be ***********************************************************************/ vint Utf16BEEncoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) { #if defined VCZH_WCHAR_UTF16 if (freeToUpdate) { SwapBytesForUtf16BE(_buffer, chars); vint size = chars * sizeof(wchar_t); vint written = stream->Write(_buffer, size); SwapBytesForUtf16BE(_buffer, chars); if (written != size) { Close(); return 0; } return chars; } else { vint counter = 0; for (vint i = 0; i < chars; i++) { wchar_t c = _buffer[i]; SwapByteForUtf16BE(c); vint written = stream->Write(&c, sizeof(c)); if (written != sizeof(c)) { Close(); return 0; } counter++; } return counter; } #elif defined VCZH_WCHAR_UTF32 WCharToUtfReader reader(_buffer, chars); while (char16_t c = reader.Read()) { SwapByteForUtf16BE(c); vint written = stream->Write(&c, sizeof(c)); if (written != sizeof(c)) { Close(); return 0; } } if (reader.HasIllegalChar()) { Close(); return 0; } return chars; #endif } vint Utf16BEDecoder::ReadString(wchar_t* _buffer, vint chars) { #if defined VCZH_WCHAR_UTF16 vint read = stream->Read(_buffer, chars * sizeof(wchar_t)); CHECK_ERROR(read % sizeof(wchar_t) == 0, L"Utf16Decoder::ReadString(wchar_t*, vint)#Failed to read complete wchar_t characters."); vint readChars = read / sizeof(wchar_t); SwapBytesForUtf16BE(_buffer, readChars); return readChars; #elif defined VCZH_WCHAR_UTF32 reader.Setup(stream); vint counter = 0; for (vint i = 0; i < chars; i++) { wchar_t c = reader.Read(); if (!c) break; _buffer[i] = c; counter++; } return counter; #endif } /*********************************************************************** Utf8 ***********************************************************************/ vint Utf8Decoder::ReadString(wchar_t* _buffer, vint chars) { reader.Setup(stream); vint counter = 0; for (vint i = 0; i < chars; i++) { wchar_t c = reader.Read(); if (!c) break; _buffer[i] = c; counter++; } return counter; } /*********************************************************************** Utf-32 ***********************************************************************/ vint Utf32Encoder::WriteString(wchar_t* _buffer, vint chars, bool freeToUpdate) { #if defined VCZH_WCHAR_UTF16 WCharTo32Reader reader(_buffer, chars); while (char32_t c = reader.Read()) { vint written = stream->Write(&c, sizeof(c)); if (written != sizeof(c)) { Close(); return 0; } } if (reader.HasIllegalChar()) { Close(); return 0; } return chars; #elif defined VCZH_WCHAR_UTF32 vint size = chars * sizeof(wchar_t); vint written = stream->Write(_buffer, size); if (written != size) { Close(); return 0; } return chars; #endif } vint Utf32Decoder::ReadString(wchar_t* _buffer, vint chars) { #if defined VCZH_WCHAR_UTF16 reader.Setup(stream); vint counter = 0; for (vint i = 0; i < chars; i++) { wchar_t c = reader.Read(); if (!c) break; _buffer[i] = c; counter++; } return counter; #elif defined VCZH_WCHAR_UTF32 vint read = stream->Read(_buffer, chars * sizeof(wchar_t)); CHECK_ERROR(read % sizeof(wchar_t) == 0, L"Utf16Decoder::ReadString(wchar_t*, vint)#Failed to read complete wchar_t characters."); return read / sizeof(wchar_t); #endif } } } /*********************************************************************** .\STREAM\CHARFORMAT_BOM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** BomEncoder ***********************************************************************/ BomEncoder::BomEncoder(Encoding _encoding) :encoding(_encoding) ,encoder(0) { switch(encoding) { case Mbcs: encoder=new MbcsEncoder; break; case Utf8: encoder=new Utf8Encoder; break; case Utf16: encoder=new Utf16Encoder; break; case Utf16BE: encoder=new Utf16BEEncoder; break; } } BomEncoder::~BomEncoder() { Close(); } void BomEncoder::Setup(IStream* _stream) { switch(encoding) { case Mbcs: break; case Utf8: _stream->Write((void*)"\xEF\xBB\xBF", 3); break; case Utf16: _stream->Write((void*)"\xFF\xFE", 2); break; case Utf16BE: _stream->Write((void*)"\xFE\xFF", 2); break; } encoder->Setup(_stream); } void BomEncoder::Close() { if(encoder) { encoder->Close(); delete encoder; encoder=0; } } vint BomEncoder::Write(void* _buffer, vint _size) { return encoder->Write(_buffer, _size); } /*********************************************************************** BomDecoder ***********************************************************************/ BomDecoder::BomStream::BomStream(IStream* _stream, char* _bom, vint _bomLength) :stream(_stream) ,bomPosition(0) ,bomLength(_bomLength) { memcpy(bom, _bom, bomLength); } bool BomDecoder::BomStream::CanRead()const { return IsAvailable(); } bool BomDecoder::BomStream::CanWrite()const { return false; } bool BomDecoder::BomStream::CanSeek()const { return false; } bool BomDecoder::BomStream::CanPeek()const { return false; } bool BomDecoder::BomStream::IsLimited()const { return stream!=0 && stream->IsLimited(); } bool BomDecoder::BomStream::IsAvailable()const { return stream!=0 && stream->IsAvailable(); } void BomDecoder::BomStream::Close() { stream=0; } pos_t BomDecoder::BomStream::Position()const { return IsAvailable()?bomPosition+stream->Position():-1; } pos_t BomDecoder::BomStream::Size()const { return -1; } void BomDecoder::BomStream::Seek(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::Seek(pos_t)#Operation not supported."); } void BomDecoder::BomStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::SeekFromBegin(pos_t)#Operation not supported."); } void BomDecoder::BomStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"BomDecoder::BomStream::SeekFromEnd(pos_t)#Operation not supported."); } vint BomDecoder::BomStream::Read(void* _buffer, vint _size) { vint result=0; unsigned char* buffer=(unsigned char*)_buffer; if(bomPositionRead(buffer, _size); } return result; } vint BomDecoder::BomStream::Write(void* _buffer, vint _size) { CHECK_FAIL(L"BomDecoder::BomStream::Write(void*, vint)#Operation not supported."); } vint BomDecoder::BomStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"BomDecoder::BomStream::Peek(void*, vint)#Operation not supported."); } BomDecoder::BomDecoder() :decoder(0) { } BomDecoder::~BomDecoder() { Close(); } void BomDecoder::Setup(IStream* _stream) { char bom[3]={0}; vint length=_stream->Read(bom, sizeof(bom)); if(strncmp(bom, "\xEF\xBB\xBF", 3)==0) { decoder=new Utf8Decoder; stream=new BomStream(_stream, bom+3, 0); } else if(strncmp(bom, "\xFF\xFE", 2)==0) { decoder=new Utf16Decoder; stream=new BomStream(_stream, bom+2, 1); } else if(strncmp(bom, "\xFE\xFF", 2)==0) { decoder=new Utf16BEDecoder; stream=new BomStream(_stream, bom+2, 1); } else { decoder=new MbcsDecoder; stream=new BomStream(_stream, bom, 3); } decoder->Setup(stream); } void BomDecoder::Close() { if(decoder) { decoder->Close(); delete decoder; decoder=0; stream->Close(); delete stream; stream=0; } } vint BomDecoder::Read(void* _buffer, vint _size) { return decoder->Read(_buffer, _size); } } } /*********************************************************************** .\STREAM\CHARFORMAT_TESTENCODING.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** Helper Functions ***********************************************************************/ bool CanBeMbcs(unsigned char* buffer, vint size) { for(vint i=0;i= 3 && strncmp((char*)buffer, "\xEF\xBB\xBF", 3) == 0) { encoding = BomEncoder::Utf8; containsBom = true; } else if (size >= 2 && strncmp((char*)buffer, "\xFF\xFE", 2) == 0) { encoding = BomEncoder::Utf16; containsBom = true; } else if (size >= 2 && strncmp((char*)buffer, "\xFE\xFF", 2) == 0) { encoding = BomEncoder::Utf16BE; containsBom = true; } else { encoding = BomEncoder::Mbcs; containsBom = false; bool utf16HitSurrogatePairs = false; bool utf16BEHitSurrogatePairs = false; bool roughMbcs = CanBeMbcs(buffer, size); bool roughUtf8 = CanBeUtf8(buffer, size); bool roughUtf16 = CanBeUtf16(buffer, size, utf16HitSurrogatePairs); bool roughUtf16BE = CanBeUtf16BE(buffer, size, utf16BEHitSurrogatePairs); vint roughCount = (roughMbcs ? 1 : 0) + (roughUtf8 ? 1 : 0) + (roughUtf16 ? 1 : 0) + (roughUtf16BE ? 1 : 0); if (roughCount == 1) { if (roughUtf8) encoding = BomEncoder::Utf8; else if (roughUtf16) encoding = BomEncoder::Utf16; else if (roughUtf16BE) encoding = BomEncoder::Utf16BE; } else if (roughCount > 1) { TestEncodingInternal(buffer, size, encoding, containsBom, utf16HitSurrogatePairs, utf16BEHitSurrogatePairs, roughMbcs, roughUtf8, roughUtf16, roughUtf16BE); } } } } } /*********************************************************************** .\STREAM\COMPRESSIONSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { using namespace collections; using namespace lzw; /*********************************************************************** LzwBase ***********************************************************************/ void LzwBase::UpdateIndexBits() { if (nextIndex >=2 && (nextIndex & (nextIndex - 1)) == 0) { indexBits++; } } lzw::Code* LzwBase::CreateCode(lzw::Code* prefix, vuint8_t byte) { if (nextIndex < MaxDictionarySize) { Code* code = codeAllocator.Create(); code->byte = byte; code->code = nextIndex; code->parent = prefix; code->size = prefix->size + 1; prefix->children.Set(byte, code, mapAllocator); nextIndex++; return code; } else { return 0; } } LzwBase::LzwBase() :codeAllocator(65536) , mapAllocator(1048576) { root = codeAllocator.Create(); for (vint i = 0; i < 256; i++) { UpdateIndexBits(); CreateCode(root, (vuint8_t)i); } } LzwBase::LzwBase(bool (&existingBytes)[256]) { root = codeAllocator.Create(); for (vint i = 0; i < 256; i++) { if (existingBytes[i]) { UpdateIndexBits(); CreateCode(root, (vuint8_t)i); } } if (indexBits < 8) { eofIndex = nextIndex++; } } LzwBase::~LzwBase() { } /*********************************************************************** LzwEncoder ***********************************************************************/ void LzwEncoder::Flush() { vint written = 0; vint bufferUsedSize = bufferUsedBits / 8; if (bufferUsedBits % 8 != 0) { bufferUsedSize++; } while (written < bufferUsedSize) { vint size = stream->Write(buffer + written, bufferUsedSize - written); CHECK_ERROR(size != 0, L"LzwEncoder::Flush()#Failed to flush the lzw buffer."); written += size; } bufferUsedBits = 0; } vuint8_t highMarks[9] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF }; vuint8_t lowMarks[9] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF }; void LzwEncoder::WriteNumber(vint number, vint bitSize) { vint bitStart = 0; vint bitStep = 8 - bufferUsedBits % 8; if (bitStep > bitSize) { bitStep = bitSize; } while (bitStart < bitSize) { if(bufferUsedBits == BufferSize * 8) { Flush(); } vint writeStart = bufferUsedBits % 8; vint byteIndex = bufferUsedBits / 8; vuint8_t byte = buffer[byteIndex]; byte &= highMarks[writeStart]; vuint8_t content = (vuint8_t)((number >> bitStart)&lowMarks[bitStep]) << (8 - writeStart - bitStep); byte |= content; buffer[byteIndex] = byte; bufferUsedBits += bitStep; bitStart += bitStep; vint remain = bitSize - bitStart; bitStep = remain < 8 ? remain : 8; } } LzwEncoder::LzwEncoder() { prefix = root; } LzwEncoder::LzwEncoder(bool (&existingBytes)[256]) :LzwBase(existingBytes) { prefix = root; } LzwEncoder::~LzwEncoder() { } void LzwEncoder::Setup(IStream* _stream) { stream = _stream; } void LzwEncoder::Close() { if (prefix != root) { WriteNumber(prefix->code, indexBits); prefix = root; } vint remain = 8 - bufferUsedBits % 8; if (remain != 8 && remain >= indexBits) { CHECK_ERROR(eofIndex != -1, L"LzwEncoder::Close()#Internal error."); WriteNumber(eofIndex, indexBits); } Flush(); } vint LzwEncoder::Write(void* _buffer, vint _size) { vuint8_t* bytes = (vuint8_t*)_buffer; for (vint i = 0; i < _size; i++) { vuint8_t byte = bytes[i]; Code* next = prefix->children.Get(byte); if (next) { prefix = next; } else { WriteNumber(prefix->code, indexBits); if (nextIndex < MaxDictionarySize) { UpdateIndexBits(); CreateCode(prefix, byte); } prefix = root->children.Get(byte); } } return _size; } /*********************************************************************** LzwDecoder ***********************************************************************/ bool LzwDecoder::ReadNumber(vint& number, vint bitSize) { number = 0; if (inputBufferSize == -1) { return false; } vint remainBits = inputBufferSize * 8 - inputBufferUsedBits; vint writtenBits = 0; vint bitStep = 8 - inputBufferUsedBits % 8; if (bitStep > bitSize) { bitStep = bitSize; } while (writtenBits < bitSize) { if (remainBits == 0) { inputBufferSize = stream->Read(inputBuffer, BufferSize); if (inputBufferSize == 0) { inputBufferSize = -1; return false; } remainBits = inputBufferSize * 8; inputBufferUsedBits = 0; } vuint8_t byte = inputBuffer[inputBufferUsedBits / 8]; byte >>= (8 - inputBufferUsedBits % 8 - bitStep); byte &= lowMarks[bitStep]; number |= byte << writtenBits; inputBufferUsedBits += bitStep; remainBits -= bitStep; writtenBits += bitStep; vint remain = bitSize - writtenBits; bitStep = remain < 8 ? remain : 8; } return true; } void LzwDecoder::PrepareOutputBuffer(vint size) { if (outputBuffer.Count() < size) { outputBuffer.Resize(size); } outputBufferSize = size; } void LzwDecoder::ExpandCodeToOutputBuffer(lzw::Code* code) { vuint8_t* outputByte = &outputBuffer[0] + code->size; Code* current = code; while (current != root) { *(--outputByte) = current->byte; current = current->parent; } outputBufferUsedBytes = 0; } LzwDecoder::LzwDecoder() { for (vint i = 0; i < 256; i++) { dictionary.Add(root->children.Get((vuint8_t)i)); } } LzwDecoder::LzwDecoder(bool (&existingBytes)[256]) :LzwBase(existingBytes) { for (vint i = 0; i < 256; i++) { if (existingBytes[i]) { dictionary.Add(root->children.Get((vuint8_t)i)); } } if (eofIndex != -1) { dictionary.Add(nullptr); } } LzwDecoder::~LzwDecoder() { } void LzwDecoder::Setup(IStream* _stream) { stream = _stream; } void LzwDecoder::Close() { } vint LzwDecoder::Read(void* _buffer, vint _size) { vint written = 0; vuint8_t* bytes = (vuint8_t*)_buffer; while (written < _size) { vint expect = _size - written; vint remain = outputBufferSize - outputBufferUsedBytes; if (remain == 0) { vint index = 0; if (!ReadNumber(index, indexBits) || index == eofIndex) { break; } Code* prefix = 0; if (index == dictionary.Count()) { prefix = lastCode; PrepareOutputBuffer(prefix->size + 1); ExpandCodeToOutputBuffer(prefix); outputBuffer[outputBufferSize - 1] = outputBuffer[0]; } else { prefix = dictionary[index]; PrepareOutputBuffer(prefix->size); ExpandCodeToOutputBuffer(prefix); } if (nextIndex < MaxDictionarySize) { if (lastCode) { dictionary.Add(CreateCode(lastCode, outputBuffer[0])); } UpdateIndexBits(); } lastCode = dictionary[index]; } else { if (remain > expect) { remain = expect; } memcpy(bytes + written, &outputBuffer[outputBufferUsedBytes], remain); outputBufferUsedBytes += remain; written += remain; } } return written; } /*********************************************************************** Helper Functions ***********************************************************************/ vint CopyStream(stream::IStream& inputStream, stream::IStream& outputStream) { vint totalSize = 0; while (true) { char buffer[1024]; vint copied = inputStream.Read(buffer, (vint)sizeof(buffer)); if (copied == 0) { break; } totalSize += outputStream.Write(buffer, copied); } return totalSize; } const vint CompressionFragmentSize = 1048576; void CompressStream(stream::IStream& inputStream, stream::IStream& outputStream) { Array buffer(CompressionFragmentSize); while (true) { vint size = inputStream.Read(&buffer[0], buffer.Count()); if (size == 0) break; MemoryStream compressedStream; { LzwEncoder encoder; EncoderStream encoderStream(compressedStream, encoder); encoderStream.Write(&buffer[0], size); } compressedStream.SeekFromBegin(0); { { vint32_t bufferSize = (vint32_t)size; outputStream.Write(&bufferSize, (vint)sizeof(bufferSize)); } { vint32_t compressedSize = (vint32_t)compressedStream.Size(); outputStream.Write(&compressedSize, (vint)sizeof(compressedSize)); } CopyStream(compressedStream, outputStream); } } } void DecompressStream(stream::IStream& inputStream, stream::IStream& outputStream) { vint totalSize = 0; vint totalWritten = 0; while (true) { vint32_t bufferSize = 0; if (inputStream.Read(&bufferSize, (vint)sizeof(bufferSize)) != sizeof(bufferSize)) { break; } vint32_t compressedSize = 0; CHECK_ERROR(inputStream.Read(&compressedSize, (vint)sizeof(compressedSize)) == sizeof(compressedSize), L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); Array buffer(compressedSize); CHECK_ERROR(inputStream.Read(&buffer[0], compressedSize) == compressedSize, L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); MemoryWrapperStream compressedStream(&buffer[0], compressedSize); LzwDecoder decoder; DecoderStream decoderStream(compressedStream, decoder); totalWritten += CopyStream(decoderStream, outputStream); totalSize += bufferSize; } CHECK_ERROR(outputStream.Size() == totalSize, L"vl::stream::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input"); } } } /*********************************************************************** .\STREAM\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 stream { #if defined VCZH_GCC void _fseeki64(FILE* file, pos_t offset, int origin) { fseek(file, (long)offset, origin); } #endif /*********************************************************************** FileStream ***********************************************************************/ FileStream::FileStream(const WString& fileName, AccessRight _accessRight) :accessRight(_accessRight) { const wchar_t* mode=L"rb"; switch(accessRight) { case ReadOnly: mode=L"rb"; break; case WriteOnly: mode=L"wb"; break; case ReadWrite: mode=L"w+b"; break; } #if defined VCZH_MSVC if(_wfopen_s(&file, fileName.Buffer(), mode)!=0) { file=0; } #elif defined VCZH_GCC AString fileNameA = wtoa(fileName); AString modeA = wtoa(mode); file = fopen(fileNameA.Buffer(), modeA.Buffer()); #endif } FileStream::~FileStream() { Close(); } bool FileStream::CanRead()const { return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); } bool FileStream::CanWrite()const { return file!=0 && (accessRight==WriteOnly || accessRight==ReadWrite); } bool FileStream::CanSeek()const { return file!=0; } bool FileStream::CanPeek()const { return file!=0 && (accessRight==ReadOnly || accessRight==ReadWrite); } bool FileStream::IsLimited()const { return file!=0 && accessRight==ReadOnly; } bool FileStream::IsAvailable()const { return file!=0; } void FileStream::Close() { if(file!=0) { fclose(file); file=0; } } pos_t FileStream::Position()const { if(file!=0) { #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { return position; } #elif defined VCZH_GCC return (pos_t)ftell(file); #endif } return -1; } pos_t FileStream::Size()const { if(file!=0) { #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { if(fseek(file, 0, SEEK_END)==0) { pos_t size=Position(); if(fsetpos(file, &position)==0) { return size; } } } #elif defined VCZH_GCC long position = ftell(file); fseek(file, 0, SEEK_END); long size=ftell(file); fseek(file, position, SEEK_SET); return (pos_t)size; #endif } return -1; } void FileStream::Seek(pos_t _size) { if(Position()+_size>Size()) { _fseeki64(file, 0, SEEK_END); } else if(Position()+_size<0) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, _size, SEEK_CUR); } } void FileStream::SeekFromBegin(pos_t _size) { if(_size>Size()) { _fseeki64(file, 0, SEEK_END); } else if(_size<0) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, _size, SEEK_SET); } } void FileStream::SeekFromEnd(pos_t _size) { if(_size<0) { _fseeki64(file, 0, SEEK_END); } else if(_size>Size()) { _fseeki64(file, 0, SEEK_SET); } else { _fseeki64(file, -_size, SEEK_END); } } vint FileStream::Read(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Read(void*, vint)#Argument size cannot be negative."); return fread(_buffer, 1, _size, file); } vint FileStream::Write(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Write(void*, vint)#Argument size cannot be negative."); return fwrite(_buffer, 1, _size, file); } vint FileStream::Peek(void* _buffer, vint _size) { CHECK_ERROR(file!=0, L"FileStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"FileStream::Peek(void*, vint)#Argument size cannot be negative."); #if defined VCZH_MSVC fpos_t position=0; if(fgetpos(file, &position)==0) { size_t count=fread(_buffer, 1, _size, file); if(fsetpos(file, &position)==0) { return count; } } return -1; #elif defined VCZH_GCC long position=ftell(file); size_t count=fread(_buffer, 1, _size, file); fseek(file, position, SEEK_SET); return count; #endif } } } /*********************************************************************** .\STREAM\MEMORYSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** MemoryStream ***********************************************************************/ void MemoryStream::PrepareSpace(vint totalSpace) { if(totalSpace>capacity) { totalSpace=(totalSpace/block+1)*block; char* newBuffer=new char[totalSpace]; if(buffer) { memcpy(newBuffer, buffer, size); delete[] buffer; } buffer=newBuffer; capacity=totalSpace; } } MemoryStream::MemoryStream(vint _block) :block(_block) ,buffer(0) ,size(0) ,position(0) ,capacity(0) { if(block<=0) { block=65536; } } MemoryStream::~MemoryStream() { Close(); } bool MemoryStream::CanRead()const { return block!=0; } bool MemoryStream::CanWrite()const { return block!=0; } bool MemoryStream::CanSeek()const { return block!=0; } bool MemoryStream::CanPeek()const { return block!=0; } bool MemoryStream::IsLimited()const { return false; } bool MemoryStream::IsAvailable()const { return block!=0; } void MemoryStream::Close() { if(buffer) { delete[] buffer; } block=0; buffer=0; size=-1; position=-1; capacity=0; } pos_t MemoryStream::Position()const { return position; } pos_t MemoryStream::Size()const { return size; } void MemoryStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void MemoryStream::SeekFromBegin(pos_t _size) { CHECK_ERROR(block!=0, L"MemoryStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); vint expected=(vint)_size; if(expected<0) { position=0; } else if(expected>=size) { position=size; } else { position=expected; } } void MemoryStream::SeekFromEnd(pos_t _size) { SeekFromBegin(size-_size); } vint MemoryStream::Read(void* _buffer, vint _size) { CHECK_ERROR(block!=0, L"MemoryStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryStream::Read(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); position+=_size; return _size; } vint MemoryStream::Write(void* _buffer, vint _size) { CHECK_ERROR(block!=0, L"MemoryStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryStream::Write(void*, vint)#Argument size cannot be negative."); PrepareSpace(size+_size); memmove(buffer+position, _buffer, _size); position+=_size; if(size=0, L"MemoryStream::Peek(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); return _size; } void* MemoryStream::GetInternalBuffer() { return buffer; } } } /*********************************************************************** .\STREAM\MEMORYWRAPPERSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** MemoryWrapperStream ***********************************************************************/ MemoryWrapperStream::MemoryWrapperStream(void* _buffer, vint _size) :buffer((char*)_buffer) ,size(_size) ,position(0) { if(size<=0) { buffer=0; size=0; } } MemoryWrapperStream::~MemoryWrapperStream() { } bool MemoryWrapperStream::CanRead()const { return buffer!=0; } bool MemoryWrapperStream::CanWrite()const { return buffer!=0; } bool MemoryWrapperStream::CanSeek()const { return buffer!=0; } bool MemoryWrapperStream::CanPeek()const { return buffer!=0; } bool MemoryWrapperStream::IsLimited()const { return buffer!=0; } bool MemoryWrapperStream::IsAvailable()const { return buffer!=0; } void MemoryWrapperStream::Close() { buffer=0; size=-1; position=-1; } pos_t MemoryWrapperStream::Position()const { return position; } pos_t MemoryWrapperStream::Size()const { return size; } void MemoryWrapperStream::Seek(pos_t _size) { SeekFromBegin(position+_size); } void MemoryWrapperStream::SeekFromBegin(pos_t _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::SeekFromBegin(pos_t)#Stream is closed, cannot perform this operation."); vint expected=(vint)_size; if(expected<0) { position=0; } else if(expected>=size) { position=size; } else { position=expected; } } void MemoryWrapperStream::SeekFromEnd(pos_t _size) { SeekFromBegin(size-_size); } vint MemoryWrapperStream::Read(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Read(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Read(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); position+=_size; return _size; } vint MemoryWrapperStream::Write(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Write(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Write(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(buffer+position, _buffer, _size); position+=_size; return _size; } vint MemoryWrapperStream::Peek(void* _buffer, vint _size) { CHECK_ERROR(buffer!=0, L"MemoryWrapperStream::Peek(pos_t)#Stream is closed, cannot perform this operation."); CHECK_ERROR(_size>=0, L"MemoryWrapperStream::Peek(void*, vint)#Argument size cannot be negative."); vint max=size-position; if(_size>max) { _size=max; } memmove(_buffer, buffer+position, _size); return _size; } } } /*********************************************************************** .\STREAM\RECORDERSTREAM.CPP ***********************************************************************/ /*********************************************************************** Author: Zihan Chen (vczh) Licensed under https://github.com/vczh-libraries/License ***********************************************************************/ namespace vl { namespace stream { /*********************************************************************** RecorderStream ***********************************************************************/ RecorderStream::RecorderStream(IStream& _in, IStream& _out) :in(&_in) , out(&_out) { } RecorderStream::~RecorderStream() { } bool RecorderStream::CanRead()const { return IsAvailable() && in->CanRead(); } bool RecorderStream::CanWrite()const { return false; } bool RecorderStream::CanSeek()const { return false; } bool RecorderStream::CanPeek()const { return false; } bool RecorderStream::IsLimited()const { return IsAvailable() && in->IsLimited(); } bool RecorderStream::IsAvailable()const { return in != 0 && out != 0 && in->IsAvailable() && out->IsAvailable(); } void RecorderStream::Close() { in = nullptr; out = nullptr; } pos_t RecorderStream::Position()const { return IsAvailable() ? in->Position() : -1; } pos_t RecorderStream::Size()const { return IsAvailable() ? in->Size() : -1; } void RecorderStream::Seek(pos_t _size) { CHECK_FAIL(L"RecorderStream::Seek(pos_t)#Operation not supported."); } void RecorderStream::SeekFromBegin(pos_t _size) { CHECK_FAIL(L"RecorderStream::SeekFromBegin(pos_t)#Operation not supported."); } void RecorderStream::SeekFromEnd(pos_t _size) { CHECK_FAIL(L"RecorderStream::SeekFromEnd(pos_t)#Operation not supported."); } vint RecorderStream::Read(void* _buffer, vint _size) { _size = in->Read(_buffer, _size); vint written = out->Write(_buffer, _size); CHECK_ERROR(written == _size, L"RecorderStream::Read(void*, vint)#Failed to copy data to the output stream."); return _size; } vint RecorderStream::Write(void* _buffer, vint _size) { CHECK_FAIL(L"RecorderStream::Write(void*, vint)#Operation not supported."); } vint RecorderStream::Peek(void* _buffer, vint _size) { CHECK_FAIL(L"RecorderStream::Peek(void*, vint)#Operation not supported."); } } }