Files
GacUI/Import/VlppOS.cpp
2023-07-06 01:50:42 -07:00

3574 lines
77 KiB
C++

/***********************************************************************
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(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();
}
}
}
/***********************************************************************
.\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 <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;
}
}
}
/***********************************************************************
.\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;
/***********************************************************************
TextReader
***********************************************************************/
WString TextReader::ReadString(vint length)
{
wchar_t* buffer=new wchar_t[length+1];
vint i=0;
for(;i<length;i++)
{
if((buffer[i]=ReadChar())==L'\0')
{
break;
}
}
buffer[i]=L'\0';
WString result(buffer);
delete[] buffer;
return result;
}
WString TextReader::ReadLine()
{
WString result;
auto buffer = new wchar_t[65537];
buffer[0]=L'\0';
vint i=0;
while(true)
{
wchar_t c=ReadChar();
if(c==L'\n' || 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;
if(result.Length()>0 && 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<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"");
}
}
}
using namespace monospace_tabling;
void TextWriter::WriteMonospacedEnglishTable(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=(vint)wcslen(reading)+2;
if(width<length) width=length;
reading=0;
}
}
if(rowHeights[i]<height) rowHeights[i]=height;
if(columnWidths[j]<width) columnWidths[j]=width;
}
}
WriteBorderLine(*this, columnWidths, columns);
for(vint i=0;i<rows;i++)
{
WriteContentLine(*this, columnWidths, rowHeights[i], columns, tableByRow, i);
WriteBorderLine(*this, columnWidths, columns);
}
}
/***********************************************************************
StringReader
***********************************************************************/
void StringReader::PrepareIfLastCallIsReadLine()
{
if(lastCallIsReadLine)
{
lastCallIsReadLine=false;
if(current<string.Length() && string[current]==L'\r') current++;
if(current<string.Length() && string[current]==L'\n') current++;
}
}
StringReader::StringReader(const WString& _string)
:string(_string)
,current(0)
,lastCallIsReadLine(false)
{
}
bool StringReader::IsEnd()
{
return current==string.Length();
}
wchar_t StringReader::ReadChar()
{
PrepareIfLastCallIsReadLine();
if(IsEnd())
{
return L'\0';
}
else
{
return string[current++];
}
}
WString StringReader::ReadString(vint length)
{
PrepareIfLastCallIsReadLine();
if(IsEnd())
{
return L"";
}
else
{
vint remain=string.Length()-current;
if(length>remain) 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(lineEnd<string.Length())
{
wchar_t c=string[lineEnd];
if(c==L'\r' || c==L'\n') break;
lineEnd++;
}
WString result=string.Sub(current, lineEnd-current);
current=lineEnd;
lastCallIsReadLine=true;
return result;
}
}
WString StringReader::ReadToEnd()
{
return ReadString(string.Length()-current);
}
/***********************************************************************
StreamReader
***********************************************************************/
StreamReader::StreamReader(IStream& _stream)
:stream(&_stream)
{
}
bool StreamReader::IsEnd()
{
return stream==0;
}
wchar_t StreamReader::ReadChar()
{
if(stream)
{
wchar_t buffer=0;
if(stream->Read(&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;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\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<char16_t> 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<char16_t> 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(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);
}
}
}
/***********************************************************************
.\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<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);
}
}
}
}
}
/***********************************************************************
.\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<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");
}
}
}
/***********************************************************************
.\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<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.");
}
}
}