ultimatepp/uppsrc/Core/Stream.cpp
Mirek Fidler 34ff691308 sizeof(wchar) is changed to 4 (32 bits) to support non BMP unicode characters
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which
  escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application
  is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar
  especially type casts.

  To support host APIs, char16 is introduced (but there is no 16-bit String varian).

  Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API.

- Support of drawing non-BMP characters in GUI
- Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used)
- Last instances of Win32 ANSI calls (those ending with A) are removed
- UTF handling routines are refactored and their's naming is unified
- RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText)

Other minor changes:

- fixed TryRealloc issue
- improved MemoryCheck
- Removed MemoryAlloc48/MemoryFree48
- In theide Background parsing should less often cause delays in the main thread
2021-12-02 12:03:19 +01:00

1393 lines
25 KiB
C++

#include "Core.h"
#ifdef PLATFORM_POSIX
#include <sys/mman.h>
#include <termios.h>
#endif
namespace Upp {
#define LLOG(x) // RLOG(x)
#define LDUMP(x) // RDUMP(x)
#define LLOGHEXDUMP(x, y) // RLOGHEXDUMP(x, y)
void Stream::_Put(const void *data, dword size) {
const byte *s = (const byte *) data;
while(size--)
Put(*s++);
}
dword Stream::_Get(void *data, dword size) {
int c;
byte *s = (byte *) data;
dword sz;
for(sz = 0; sz < size && (c = Get()) >= 0; sz++)
*s++ = c;
return sz;
}
void Stream::_Put(int w) {
SetError(ERROR_NOT_ENOUGH_SPACE);
}
int Stream::_Get() {
return -1;
}
int Stream::_Term() {
return -1;
}
void Stream::Seek(int64) {
NEVER();
}
int Stream::Skip(int size)
{
int r = 0;
while(size) {
int n = min(int(rdlim - ptr), size);
if(n == 0) {
if(Get() < 0)
break;
r++;
size--;
}
else {
size -= n;
r += n;
ptr += n;
}
}
return r;
}
int64 Stream::GetSize() const {
return 0;
}
void Stream::SetSize(int64) {
NEVER();
}
bool Stream::IsOpen() const { return false; }
void Stream::Close() {}
void Stream::Flush() {}
Stream::Stream() {
pos = style = 0;
buffer = NULL;
ptr = rdlim = wrlim = NULL;
}
Stream::~Stream() {}
void Stream::LoadError() {
SetError(ERROR_LOADING_FAILED);
if(style & STRM_THROW)
throw LoadingError();
}
String Stream::GetErrorText() const
{
return IsError() ? Upp::GetErrorMessage(errorcode) : String();
}
bool Stream::GetAll(void *data, int size) {
if(Get(data, size) != size) {
LoadError();
return false;
}
return true;
}
void Stream::Put64(const void *data, int64 size)
{
#ifdef CPU_64
byte *ptr = (byte *)data;
while(size > INT_MAX) {
Put(ptr, INT_MAX);
ptr += INT_MAX;
size -= INT_MAX;
}
Put(ptr, (int)size);
#else
ASSERT(size <= INT_MAX);
Put(data, (int)size);
#endif
}
int64 Stream::Get64(void *data, int64 size)
{
#ifdef CPU_64
byte *ptr = (byte *)data;
int64 n = 0;
while(size > INT_MAX) {
int q = Get(ptr, INT_MAX);
n += q;
if(q != INT_MAX)
return n;
ptr += INT_MAX;
size -= INT_MAX;
}
int q = Get(ptr, (int)size);
return n + q;
#else
ASSERT(size <= INT_MAX);
return Get(data, (int)size);
#endif
}
bool Stream::GetAll64(void *data, int64 size)
{
if(Get64(data, size) != size) {
LoadError();
return false;
}
return true;
}
size_t Stream::Get(Huge& h, size_t size)
{
while(h.GetSize() < size) {
int sz = (int)min((size_t)h.CHUNK, size - h.GetSize());
int len = Get(h.AddChunk(), sz);
if(len < h.CHUNK) {
h.Finish(len);
break;
}
}
return h.GetSize();
}
bool Stream::GetAll(Huge& h, size_t size)
{
if(Get(h, size) != size) {
LoadError();
return false;
}
return true;
}
String Stream::Get(int size)
{
StringBuffer b(size);
int n = Get(~b, size);
b.SetCount(n);
return String(b);
}
String Stream::GetAll(int size)
{
String result;
if(size < 4 * 1024*1024)
result = Get(size);
else {
Huge h;
Get(h, size);
result = h.Get();
}
if(result.GetCount() != size) {
LoadError();
result = String::GetVoid();
}
return result;
}
int Stream::_Get8()
{
int c = Get();
if(c < 0) {
LoadError();
return -1;
}
return c;
}
int Stream::_Get16() {
word w;
return GetAll(&w, 2) ? w : -1;
}
int Stream::_Get32() {
int l;
return GetAll(&l, 4) ? l : -1;
}
int64 Stream::_Get64() {
int64 l;
return GetAll(&l, 8) ? l : -1;
}
int Stream::GetUtf8()
{
int code = Get();
if(code <= 0) {
LoadError();
return -1;
}
if(code < 0x80)
return code;
else
if(code < 0xC2)
return -1;
else
if(code < 0xE0) {
if(IsEof()) {
LoadError();
return -1;
}
return ((code - 0xC0) << 6) + Get() - 0x80;
}
else
if(code < 0xF0) {
int c0 = Get();
int c1 = Get();
if(c1 < 0) {
LoadError();
return -1;
}
return ((code - 0xE0) << 12) + ((c0 - 0x80) << 6) + c1 - 0x80;
}
else
if(code < 0xF8) {
int c0 = Get();
int c1 = Get();
int c2 = Get();
if(c2 < 0) {
LoadError();
return -1;
}
return ((code - 0xf0) << 18) + ((c0 - 0x80) << 12) + ((c1 - 0x80) << 6) + c2 - 0x80;
}
else
if(code < 0xFC) {
int c0 = Get();
int c1 = Get();
int c2 = Get();
int c3 = Get();
if(c3 < 0) {
LoadError();
return -1;
}
return ((code - 0xF8) << 24) + ((c0 - 0x80) << 18) + ((c1 - 0x80) << 12) +
((c2 - 0x80) << 6) + c3 - 0x80;
}
else
if(code < 0xFE) {
int c0 = Get();
int c1 = Get();
int c2 = Get();
int c3 = Get();
int c4 = Get();
if(c4 < 0) {
LoadError();
return -1;
}
return ((code - 0xFC) << 30) + ((c0 - 0x80) << 24) + ((c1 - 0x80) << 18) +
((c2 - 0x80) << 12) + ((c3 - 0x80) << 6) + c4 - 0x80;
}
else {
LoadError();
return -1;
}
}
String Stream::GetLine() {
byte *q = ptr;
while(q < rdlim)
if(*q == '\n') {
String result((const char *)ptr, (int)(uintptr_t)(q - ptr - (q > ptr && q[-1] == '\r')));
ptr = q + 1;
return result;
}
else
q++;
String result((const char *)ptr, (int)(uintptr_t)(q - ptr));
ptr = q;
for(;;) {
byte *q = ptr;
while(q < rdlim && *q != '\n')
q++;
result.Cat(ptr, (int)(uintptr_t)(q - ptr));
ptr = q;
int c = Get();
if(c == '\n')
break;
if(c < 0) {
if(result.GetCount() == 0)
return String::GetVoid();
break;
}
result.Cat(c);
}
if(*result.Last() == '\r')
result.Trim(result.GetLength() - 1);
return result;
}
void Stream::PutUtf8(int c)
{
word code = c;
if(code < 0x80)
Put(code);
else
if(code < 0x800) {
Put(0xc0 | (code >> 6));
Put(0x80 | (code & 0x3f));
}
else
if((code & 0xFF00) == 0xEE00)
Put(code);
else {
Put(0xe0 | (code >> 12));
Put(0x80 | ((code >> 6) & 0x3f));
Put(0x80 | (code & 0x3f));
}
}
void Stream::Put(const char *s)
{
while(*s) Put(*s++);
}
void Stream::Put(int c, int count) {
while(count) {
int n = min(count, (int)(intptr_t)(wrlim - ptr));
if(n > 0) {
memset(ptr, c, n);
ptr += n;
count -= n;
}
else {
Put(c);
count--;
}
}
}
void Stream::PutLine(const char *s) {
Put(s);
PutEol();
}
void Stream::PutLine(const String& s) {
Put(s);
PutEol();
}
void Stream::Put(Stream& s, int64 size, dword click) {
Buffer<byte> buffer(click);
while(size) {
dword n = s.Get(buffer, (int)min<int64>(click, size));
if(n == 0)
break;
Put(~buffer, n);
size -= n;
}
}
String Stream::GetAllRLE(int size)
{
String result;
while(result.GetCount() < size) {
int c = Get();
if(c < 0)
break;
if(c == 0xcb) {
c = Get();
result.Cat(c, Get());
}
else
result.Cat(c);
}
return result.GetCount() == size ? result : String::GetVoid();
}
void Stream::SerializeRLE(byte *data, int size)
{
ASSERT(size >= 0);
if(IsError()) return;
byte *s = (byte *)data;
byte *lim = s + size;
if(IsLoading())
while(s != lim) {
if(IsEof() || s > lim) {
LoadError();
return;
}
byte c = Get();
if(c == 0xcb) {
c = Get();
int n = Get();
if(s + n > lim) {
LoadError();
return;
}
memset(s, c, n);
s += n;
}
else
*s++ = c;
}
else
while(s < lim) {
byte c = *s;
byte *t = s + 1;
byte *lm = min(s + 250, lim);
while(*t == c && t < lm)
t++;
if(t >= s + 3 || c == 0xcb) {
Put(0xcb);
Put(c);
Put(byte(t - s));
}
else {
Put(*s);
if(t == s + 2)
Put(*s);
}
if(IsError()) break;
s = t;
}
}
void Stream::SerializeRaw(byte *data, int64 size)
{
ASSERT(size >= 0);
if(IsError()) return;
if(IsLoading())
GetAll64(data, size);
else
Put64(data, size);
}
void Stream::SerializeRaw(word *data, int64 count)
{
ASSERT(count >= 0);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
SerializeRaw((byte *)data, 2 * count);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
}
void Stream::SerializeRaw(dword *data, int64 count)
{
ASSERT(count >= 0);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
SerializeRaw((byte *)data, 4 * count);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
}
void Stream::SerializeRaw(uint64 *data, int64 count)
{
ASSERT(count >= 0);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
SerializeRaw((byte *)data, 8 * count);
#ifdef CPU_BE
EndianSwap(data, count);
#endif
}
void Stream::Pack(dword& w) {
if(IsError()) return;
if(IsLoading()) {
int q = Get();
if(q < 0)
LoadError();
else {
if(q != 255)
w = q;
else
SerializeRaw(&w, 1);
}
}
else {
if(w < 255)
Put(w);
else {
Put(255);
SerializeRaw(&w, 1);
}
}
}
void Stream::Pack(bool& a, bool& b, bool& c, bool& d, bool& e, bool& f, bool& g, bool& h) {
if(IsError()) return;
if(IsLoading()) {
int ff = Get();
if(ff < 0) LoadError();
else {
a = !!(ff & 0x80);
b = !!(ff & 0x40);
c = !!(ff & 0x20);
d = !!(ff & 0x10);
e = !!(ff & 0x08);
f = !!(ff & 0x04);
g = !!(ff & 0x02);
h = !!(ff & 0x01);
}
}
else {
int ff = 0;
if(a) ff |= 0x80;
if(b) ff |= 0x40;
if(c) ff |= 0x20;
if(d) ff |= 0x10;
if(e) ff |= 0x08;
if(f) ff |= 0x04;
if(g) ff |= 0x02;
if(h) ff |= 0x01;
Put(ff);
}
}
void Stream::Pack(bool& a, bool& b, bool& c, bool& d, bool& e, bool& f, bool& g) {
bool h = false; Pack(a, b, c, d, e, f, g, h);
}
void Stream::Pack(bool& a, bool& b, bool& c, bool& d, bool& e, bool& f) {
bool h = false; Pack(a, b, c, d, e, f, h, h);
}
void Stream::Pack(bool& a, bool& b, bool& c, bool& d, bool& e) {
bool h = false; Pack(a, b, c, d, e, h, h, h);
}
void Stream::Pack(bool& a, bool& b, bool& c, bool& d) {
bool h = false; Pack(a, b, c, d, h, h, h, h);
}
void Stream::Pack(bool& a, bool& b, bool& c) {
bool h = false; Pack(a, b, c, h, h, h, h, h);
}
void Stream::Pack(bool& a, bool& b) {
bool h = false; Pack(a, b, h, h, h, h, h, h);
}
Stream& Stream::operator%(String& s) {
if(IsError()) return *this;
if(IsLoading()) {
dword len;
len = Get();
if(len != 0xff) {
if(len & 0x80) {
len &= 0x7f;
Get(); // reserved for future use... or removal
}
}
else {
len = Get32le();
if(len & 0x80000000) {
len &= 0x7fffffff;
Get(); // reserved for future use... or removal
}
}
s = GetAll(len);
if(s.IsVoid())
LoadError();
}
else {
dword len = s.GetLength();
if(len < 127)
Put(len);
else {
Put(0xff);
Put32le(len);
}
SerializeRaw((byte *)~s, len);
}
return *this;
}
Stream& Stream::operator/(String& s) {
if(IsError()) return *this;
dword len = s.GetLength();
Pack(len);
if(IsLoading()) {
s = GetAllRLE(len);
if(s.IsVoid())
LoadError();
}
else
SerializeRLE((byte *)~s, len);
return *this;
}
Stream& Stream::operator%(WString& s)
{ // we do not support BE here anymore
if(IsError()) return *this;
if(IsLoading()) {
dword len = Get();
if(len == 0xff)
len = Get32le();
String h = GetAll(len * sizeof(char16));
if(h.IsVoid())
LoadError();
else
s = ToUtf32((const char16 *)~h, len);
}
else {
Vector<char16> x = ToUtf16(s);
dword len = x.GetCount();
if(len < 0xff)
Put(len);
else {
Put(0xff);
Put32le(len);
}
SerializeRaw((byte*)x.begin(), len * sizeof(char16));
}
return *this;
}
Stream& Stream::operator/(WString& s) {
if(IsError()) return *this;
String h = ToUtf8(s);
*this / h;
s = ToUtf32(h);
return *this;
}
Stream& Stream::operator/(int& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; }
Stream& Stream::operator/(unsigned int& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; }
Stream& Stream::operator/(long& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; }
Stream& Stream::operator/(unsigned long& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; }
void Stream::Magic(dword magic) {
dword a = magic;
*this % a;
if(magic != a) LoadError();
}
// -------------------------- String stream -----------------------------
void StringStream::SetWriteBuffer()
{
buffer = (byte *)wdata.Begin();
rdlim = buffer;
wrlim = (byte *)wdata.End();
}
void StringStream::SetWriteMode()
{
if(writemode) return;
intptr_t p = ptr - buffer;
size = data.GetLength();
wdata = data;
SetWriteBuffer();
ptr = buffer + p;
writemode = true;
}
void StringStream::SetReadMode()
{
if(!writemode) return;
wdata.SetLength((dword)GetSize());
dword p = (dword)(uintptr_t)(ptr - buffer);
data = wdata;
buffer = (byte *) ~data;
ptr = buffer + p;
wrlim = buffer;
rdlim = buffer + data.GetCount();
writemode = false;
}
void StringStream::Open(const String& adata)
{
pos = 0;
data = adata;
style = STRM_READ|STRM_WRITE|STRM_SEEK|STRM_LOADING;
wdata.Clear();
buffer = (byte *) ~data;
ptr = wrlim = buffer;
rdlim = buffer + data.GetCount();
writemode = false;
ClearError();
}
void StringStream::Create()
{
Open(String());
SetStoring();
SetWriteMode();
ClearError();
}
int64 StringStream::GetSize() const
{
return writemode ? max<int64>(GetPos(), size) : data.GetLength();
}
String StringStream::GetResult()
{
SetReadMode();
return data;
}
void StringStream::_Put(const void *d, dword sz)
{
SetWriteMode();
if(ptr + sz >= wrlim) {
size_t p = ptr - buffer;
if(limit != INT_MAX && p + sz > (size_t)limit)
throw LimitExc();
if(p + sz >= INT_MAX)
Panic("2GB StringStream limit exceeded");
int len = (int32)max((int64)128, min((int64)limit, max(2 * GetSize(), GetSize() + sz)));
wdata.SetLength(len);
SetWriteBuffer();
ptr = buffer + p;
}
memcpy8(ptr, d, sz);
ptr += sz;
}
void StringStream::Reserve(int n)
{
SetWriteMode();
intptr_t p = ptr - buffer;
wdata.SetLength((int)GetSize() + n);
SetWriteBuffer();
ptr = buffer + p;
}
void StringStream::_Put(int w)
{
byte h = w;
_Put(&h, 1);
}
dword StringStream::_Get(void *data, dword sz)
{
SetReadMode();
dword read = min((dword)(uintptr_t)(rdlim - ptr), sz);
memcpy8(data, ptr, read);
ptr += read;
return read;
}
int StringStream::_Get()
{
SetReadMode();
return ptr < rdlim ? *ptr++ : -1;
}
int StringStream::_Term() {
SetReadMode();
return ptr < rdlim ? *ptr : -1;
}
void StringStream::Seek(int64 pos) {
size = (dword)GetSize();
if(pos > size) {
SetWriteMode();
size = (dword)pos;
wdata.SetLength((dword)pos + 100);
SetWriteBuffer();
}
ptr = buffer + min(GetSize(), pos);
}
void StringStream::SetSize(int64 asize) {
SetWriteMode();
dword p = (dword)(uintptr_t)GetPos();
Seek(asize);
size = (dword)asize;
Seek(min(p, size));
}
bool StringStream::IsOpen() const {
return true;
}
// -------------------- Memory read-write stream ------------------------
void MemStream::Seek(int64 pos) {
ptr = buffer + min(pos, int64(rdlim - buffer));
}
int64 MemStream::GetSize() const {
return rdlim - buffer;
}
dword MemStream::_Get(void *data, dword size) {
if(size > (dword)(intptr_t)(rdlim - ptr))
size = (dword)(intptr_t)(rdlim - ptr);
memcpy8(data, ptr, size);
ptr += size;
return size;
}
void MemStream::_Put(const void *data, dword size) {
if(size > (dword)(uintptr_t)(wrlim - ptr)) {
SetError(ERROR_NOT_ENOUGH_SPACE);
return;
}
memcpy8(ptr, data, size);
ptr += size;
}
bool MemStream::IsOpen() const {
return true;
}
void MemStream::Create(void *data, int64 size)
{
style = STRM_WRITE|STRM_READ|STRM_SEEK|STRM_LOADING;
ptr = buffer = (byte *) data;
wrlim = rdlim = buffer + (size_t)size;
pos = 0;
}
MemStream::MemStream(void *data, int64 size) {
Create(data, size);
}
MemStream::MemStream() {}
// ----------------------- Memory read streamer -------------------------
void MemReadStream::Create(const void *data, int64 size)
{
MemStream::Create((void *)data, size);
style = STRM_READ|STRM_SEEK|STRM_LOADING;
wrlim = buffer;
}
MemReadStream::MemReadStream(const void *data, int64 size)
{
Create(data, size);
}
MemReadStream::MemReadStream() {}
// --------------------------- Size stream -----------------------
int64 SizeStream::GetSize() const
{
return int64(ptr - buffer + pos);
}
void SizeStream::_Put(const void *, dword sz)
{
wrlim = buffer + sizeof(h);
pos += ptr - buffer + sz;
ptr = buffer;
}
void SizeStream::_Put(int w)
{
_Put(NULL, 1);
}
bool SizeStream::IsOpen() const
{
return true;
}
SizeStream::SizeStream()
{
style = STRM_WRITE;
buffer = ptr = h;
}
// ------------------------------ Compare stream ----------------------------
CompareStream::CompareStream() {
stream = NULL;
equal = false;
size = 0;
buffer = h;
}
CompareStream::CompareStream(Stream& astream) {
stream = NULL;
buffer = h;
Open(astream);
}
void CompareStream::Open(Stream& astream) {
ASSERT(astream.IsOpen());
Close();
style = STRM_WRITE|STRM_SEEK;
stream = &astream;
size = pos = 0;
wrlim = buffer + 128;
ptr = buffer;
equal = true;
ClearError();
}
bool CompareStream::IsOpen() const {
return !!stream;
}
int64 CompareStream::GetSize() const {
return max(int64(ptr - buffer + pos), size);
}
void CompareStream::Close() {
if(!stream) return;
if(GetPos() > size)
size = GetPos();
Flush();
if(stream->GetSize() != GetSize())
equal = false;
stream = NULL;
}
void CompareStream::SetSize(int64 asize) {
Flush();
pos += ptr - buffer;
ptr = buffer;
size = asize;
if(pos > size)
pos = size;
}
void CompareStream::Seek(int64 apos) {
Flush();
int64 sz = ptr - buffer + pos;
if(sz > size)
size = sz;
pos = apos;
ptr = buffer;
}
void CompareStream::Compare(int64 pos, const void *data, int size) {
ASSERT(stream);
if(!size) return;
Buffer<byte> b(size);
if(stream->GetPos() != pos)
stream->Seek(pos);
if(stream->Get(b, size) != size || memcmp(b.operator const byte *(), data, size))
equal = false;
}
void CompareStream::Flush() {
Compare(pos, buffer, (int)(ptr - buffer));
}
void CompareStream::_Put(const void *data, dword size) {
wrlim = buffer + sizeof(h);
ASSERT(ptr <= wrlim);
Flush();
pos += ptr - buffer;
ptr = buffer;
byte *b = (byte *) data;
while(size && equal) {
int sz = min<int>(size, sizeof(h));
Compare(pos, b, sz);
pos += sz;
b += sz;
size -= sz;
}
}
void CompareStream::_Put(int w) {
byte b = w;
_Put(&b, 1);
}
OutStream::OutStream()
{
const int bsz = 64 * 1024;
h = (byte *)MemoryAlloc(bsz);
buffer = ptr = h;
wrlim = h + bsz;
}
OutStream::~OutStream()
{ // Note: cannot call Close here !
MemoryFree(h);
}
void OutStream::_Put(int w)
{
Flush();
*ptr++ = w;
}
void OutStream::_Put(const void *data, dword size)
{
if(ptr == buffer)
Out(data, size);
else
if(ptr + size < wrlim) {
memcpy8(ptr, data, size);
ptr += size;
}
else {
Flush();
Out(data, size);
}
}
void OutStream::Flush()
{
if(ptr != buffer) {
Out(buffer, int(ptr - buffer));
ptr = h;
}
}
void OutStream::Close()
{
Flush();
}
bool OutStream::IsOpen() const
{
return true;
}
void TeeStream::Out(const void *data, dword size)
{
a.Put(data, size);
b.Put(data, size);
}
struct NilStreamClass : public Stream {
virtual void _Put(int w) {}
virtual bool IsOpen() const { return true; }
virtual int _Term() { return -1; }
virtual int _Get() { return -1; }
};
Stream& NilStream()
{
return Single<NilStreamClass>();
}
#ifndef PLATFORM_WINCE
class CoutStream : public Stream {
#ifdef PLATFORM_WIN32
String buffer;
void Flush() {
ONCELOCK {
SetConsoleOutputCP(65001); // set console to UTF8 mode
}
static HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
dword dummy;
WriteFile(h, ~buffer, buffer.GetCount(), &dummy, NULL);
buffer.Clear();
}
#endif
void Put0(int w) {
#ifdef PLATFORM_WIN32
buffer.Cat(w);
if(CheckUtf8(buffer) || buffer.GetCount() > 8)
Flush();
#else
putchar(w);
#endif
}
virtual void _Put(int w) {
if(w == '\n') {
#ifdef PLATFORM_WIN32
Put0('\r');
#endif
Put0('\n');
}
else
if(w != '\r')
Put0(w);
}
virtual bool IsOpen() const { return true; }
#ifdef PLATFORM_POSIX
virtual void Flush() { fflush(stdout); }
#endif
};
Stream& Cout()
{
return Single<CoutStream>();
}
class CerrStream : public Stream {
virtual void _Put(int w) {
#ifdef PLATFORM_WIN32
static HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
char s[1];
s[0] = w;
dword dummy;
WriteFile(h, s, 1, &dummy, NULL);
#else
putc(w, stderr);
#endif
}
#ifdef PLATFORM_POSIX
virtual void _Put(const void *data, dword size) {
fwrite(data, 1, size, stderr);
}
#endif
virtual bool IsOpen() const { return true; }
};
Stream& Cerr()
{
return Single<CerrStream>();
}
#endif
String ReadStdIn()
{
String r;
for(;;) {
int c = getchar();
if(c < 0)
return r.GetCount() ? r : String::GetVoid();
if(c == '\n')
return r;
r.Cat(c);
}
}
String ReadSecret()
{
DisableEcho();
String s = ReadStdIn();
EnableEcho();
Cout().PutEol();
return s;
}
void EnableEcho(bool b)
{
#ifdef PLATFORM_POSIX
termios t;
tcgetattr(STDIN_FILENO, &t);
if(b) t.c_lflag |= ECHO;
else t.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSADRAIN, &t);
#elif PLATFORM_WIN32
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
GetConsoleMode(h, &mode);
if(b) mode |= ENABLE_ECHO_INPUT;
else mode &= ~ENABLE_ECHO_INPUT;
SetConsoleMode(h, mode);
#endif
}
void DisableEcho()
{
EnableEcho(false);
}
// ---------------------------------------------------------------------------
String LoadStream(Stream& in) {
if(in.IsOpen()) {
in.ClearError();
int64 size = in.GetLeft();
if(size >= 0 && size < INT_MAX) {
StringBuffer s((int)size);
in.Get(s, (int)size);
if(!in.IsError())
return String(s);
}
}
return String::GetVoid();
}
String LoadFile(const char *filename) {
FindFile ff(filename);
if(ff && ff.IsFile()) {
FileIn in(filename);
return LoadStream(in);
}
return String::GetVoid();
}
bool SaveStream(Stream& out, const String& data) {
if(!out.IsOpen() || out.IsError()) return false;
out.Put((const char *)data, data.GetLength());
out.Close();
return out.IsOK();
}
bool SaveFile(const char *filename, const String& data) {
FileOut out(filename);
return SaveStream(out, data);
}
int64 CopyStream(Stream& dest, Stream& src, int64 count)
{
return CopyStream(dest, src, count, Null);
}
int64 CopyStream(Stream& dest, Stream& src, int64 count, Gate<int64, int64> progress, int chunk_size)
{
int block = (int)min<int64>(count, chunk_size);
Buffer<byte> temp(block);
int loaded;
int64 done = 0;
int64 total = count;
while(count > 0 && (loaded = src.Get(~temp, (int)min<int64>(count, block))) > 0) {
dest.Put(~temp, loaded);
if(dest.IsError())
return -1;
count -= loaded;
done += loaded;
if(progress(done, total))
return -1;
}
return done;
}
void CheckedSerialize(const Event<Stream&> serialize, Stream& stream, int version)
{
int pos = (int)stream.GetPos();
stream.Magic(0x61746164);
if(!IsNull(version))
stream.Magic(version);
serialize(stream);
stream.Magic(0x00646e65);
pos = int(stream.GetPos() - pos);
stream.Magic(pos);
}
bool Load(Event<Stream&> serialize, Stream& stream, int version) {
StringStream backup;
backup.SetStoring();
serialize(backup);
ASSERT(!backup.IsError());
stream.SetLoading();
stream.LoadThrowing();
try {
CheckedSerialize(serialize, stream, version);
}
catch(LoadingError) {
backup.Seek(0);
backup.SetLoading();
serialize(backup);
ASSERT(!backup.IsError());
return false;
}
catch(ValueTypeError) {
backup.Seek(0);
backup.SetLoading();
serialize(backup);
ASSERT(!backup.IsError());
return false;
}
return true;
}
bool Store(Event<Stream&> serialize, Stream& stream, int version) {
stream.SetStoring();
CheckedSerialize(serialize, stream, version);
return !stream.IsError();
}
String Cfgname(const char *file) {
return file ? String(file) : ConfigFile();
}
bool LoadFromFile(Event<Stream&> serialize, const char *file, int version) {
FileIn f(Cfgname(file));
return f ? Load(serialize, f, version) : false;
}
bool StoreToFile(Event<Stream&> serialize, const char *file, int version) {
FileOut f(Cfgname(file));
if(!f || !Store(serialize, f, version))
return false;
f.Close();
return !f.IsError();
}
Stream& Pack16(Stream& s, int& i) {
if(s.IsLoading()) {
i = (int16) s.Get16le();
if(i == -32768)
i = s.Get32le();
}
else
if(i < -32767 || i > 32767) {
s.Put16le((word)-32768);
s.Put32le(i);
}
else
s.Put16le((word)i);
return s;
}
Stream& Pack16(Stream& s, int& i1, int& i2) {
Pack16(s, i1);
Pack16(s, i2);
return s;
}
Stream& Pack16(Stream& s, int& i1, int& i2, int& i3) {
Pack16(s, i1, i2);
Pack16(s, i3);
return s;
}
Stream& Pack16(Stream& s, int& i1, int& i2, int& i3, int& i4) {
Pack16(s, i1, i2, i3);
Pack16(s, i4);
return s;
}
Stream& Pack16(Stream& s, int& i1, int& i2, int& i3, int& i4, int& i5) {
Pack16(s, i1, i2, i3, i4);
Pack16(s, i5);
return s;
}
int StreamHeading(Stream& stream, int ver, int minver, int maxver, const char* tag)
{
if(stream.IsLoading() && stream.IsEof() || stream.IsError())
return Null;
String text = tag;
dword len = text.GetLength();
stream.Pack(len);
if(stream.IsLoading()) {
if(stream.IsError() || (int)len != text.GetLength()) {
stream.SetError();
return Null;
}
StringBuffer b(len);
stream.SerializeRaw((byte *)~b, len);
String in = b;
if(stream.IsError() || in != text) {
stream.SetError();
return Null;
}
}
else
stream.SerializeRaw((byte *)(const char*)text, len);
stream / ver;
if(ver < minver || ver > maxver) {
stream.SetError();
return Null;
}
return ver;
}
}