diff --git a/uppsrc/Core/App.cpp b/uppsrc/Core/App.cpp index 5a547028f..032871bc0 100644 --- a/uppsrc/Core/App.cpp +++ b/uppsrc/Core/App.cpp @@ -391,12 +391,29 @@ void AppExit__() #endif } +#if defined(PLATFORM_WIN32) && !defined(PLATFORM_WINCE) +static rawthread_t rawthread__ sShellExecuteOpen(void *str) +{ + ShellExecuteW(NULL, L"open", (wchar *)str, NULL, L".", SW_SHOWDEFAULT); + free(str); + return 0; +} + +void LaunchWebBrowser(const String& url) +{ + WString wurl = ToSystemCharsetW(url); + if (int(ShellExecuteW(NULL, L"open", wurl, NULL, L".", SW_SHOWDEFAULT)) <= 32) { + int l = 2 * wurl.GetLength() + 1; + char *curl = (char *)malloc(l); + memcpy(curl, wurl, l); + StartRawThread(sShellExecuteOpen, curl); + } +} +#endif + +#ifdef PLATFORM_POSIX void LaunchWebBrowser(const String& url) { -#if defined(PLATFORM_WIN32) && !defined(PLATFORM_WINCE) - ShellExecute(NULL, "open", url, NULL, ".", SW_SHOWDEFAULT); -#endif -#ifdef PLATFORM_POSIX const char * browser[] = { "htmlview", "xdg-open", "x-www-browser", "firefox", "konqueror", "opera", "epiphany", "galeon", "netscape" }; @@ -407,8 +424,8 @@ void LaunchWebBrowser(const String& url) ); break; } -#endif } +#endif String GetDataFile(const char *filename) { diff --git a/uppsrc/Core/Mt.cpp b/uppsrc/Core/Mt.cpp index 8e230790c..266acd15c 100644 --- a/uppsrc/Core/Mt.cpp +++ b/uppsrc/Core/Mt.cpp @@ -586,4 +586,24 @@ LazyUpdate::LazyUpdate() #endif +bool StartRawThread(rawthread_t (rawthread__ *fn)(void *ptr), void *ptr) +{ +#ifdef PLATFORM_WIN32 + HANDLE handle; + handle = (HANDLE)_beginthreadex(0, 0, fn, ptr, 0, NULL); + if(handle) { + CloseHandle(handle); + return true; + } +#endif +#ifdef PLATFORM_POSIX + pthread_t handle; + if(pthread_create(&handle, 0, fn, ptr) == 0) { + pthread_detach(handle); + return true; + } +#endif + return false; +} + END_UPP_NAMESPACE diff --git a/uppsrc/Core/Mt.h b/uppsrc/Core/Mt.h index 210ed2787..a03029822 100644 --- a/uppsrc/Core/Mt.h +++ b/uppsrc/Core/Mt.h @@ -553,3 +553,45 @@ inline void AssertST() {} typedef Mutex CriticalSection; // deprecated typedef StaticMutex StaticCriticalSection; // deprecated + +// Raw multirthreading intended for use even in single-threaded applications +// to resolve some host platform issues. Raw threads cannot use U++ heap + +#ifdef PLATFORM_WIN32 +#define rawthread_t uintptr_t +#define rawthread__ __stdcall +#else +#define rawthread_t void * +#define rawthread__ +#endif + +#ifdef PLATFORM_WIN32 +struct RawMutex { + CRITICAL_SECTION cs; + + void Enter() { EnterCriticalSection(&cs); } + void Leave() { LeaveCriticalSection(&cs); } + + RawMutex() { InitializeCriticalSection(&cs); } + ~RawMutex() { DeleteCriticalSection(&cs); } +}; +#endif + +#ifdef PLATFORM_POSIX +struct RawMutex { + pthread_mutex_t mutex[1]; + + void Enter() { pthread_mutex_lock(mutex); } + void Leave() { pthread_mutex_unlock(mutex); } + + RawMutex() { + pthread_mutexattr_t mutex_attr[1]; + pthread_mutexattr_init(mutex_attr); + pthread_mutexattr_settype(mutex_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mutex, mutex_attr); + } + ~RawMutex() { pthread_mutex_destroy(mutex); } +}; +#endif + +bool StartRawThread(rawthread_t (rawthread__ *fn)(void *ptr), void *ptr); diff --git a/uppsrc/Core/SSL/SSL.h b/uppsrc/Core/SSL/SSL.h new file mode 100644 index 000000000..d675f6e0a --- /dev/null +++ b/uppsrc/Core/SSL/SSL.h @@ -0,0 +1,4 @@ +#ifndef _SSL_SSL_h +#define _SSL_SSL_h + +#endif diff --git a/uppsrc/Core/SSL/SSL.upp b/uppsrc/Core/SSL/SSL.upp new file mode 100644 index 000000000..5a7b62260 --- /dev/null +++ b/uppsrc/Core/SSL/SSL.upp @@ -0,0 +1 @@ +file "SSL.h"; diff --git a/uppsrc/Core/Socket.cpp b/uppsrc/Core/Socket.cpp index c36af2818..0802fc9e2 100644 --- a/uppsrc/Core/Socket.cpp +++ b/uppsrc/Core/Socket.cpp @@ -1,770 +1,721 @@ -#include "Core.h" - -#ifdef PLATFORM_WIN32 -#include -#include -#include -#endif - -#ifdef PLATFORM_POSIX -#include -#endif - -NAMESPACE_UPP - -#ifdef PLATFORM_WIN32 -#pragma comment(lib, "ws2_32.lib") -#endif - -#define LLOG(x) // DLOG("TCP " << x) - -#ifdef PLATFORM_WIN32 -struct RawMutex { - CRITICAL_SECTION cs; - - void Enter() { EnterCriticalSection(&cs); } - void Leave() { LeaveCriticalSection(&cs); } - - RawMutex() { InitializeCriticalSection(&cs); } - ~RawMutex() { DeleteCriticalSection(&cs); } -}; -#endif - -#ifdef PLATFORM_POSIX -struct RawMutex { - pthread_mutex_t mutex[1]; - - void Enter() { pthread_mutex_lock(mutex); } - void Leave() { pthread_mutex_unlock(mutex); } - - RawMutex() { - pthread_mutexattr_t mutex_attr[1]; - pthread_mutexattr_init(mutex_attr); - pthread_mutexattr_settype(mutex_attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(mutex, mutex_attr); - } - ~RawMutex() { pthread_mutex_destroy(mutex); } -}; -#endif - -bool StartRawThread(rawthread_t (rawthread__ *fn)(void *ptr), void *ptr) -{ -#ifdef PLATFORM_WIN32 - HANDLE handle; - handle = (HANDLE)_beginthreadex(0, 0, fn, ptr, 0, NULL); - if(handle) { - CloseHandle(handle); - return true; - } -#endif -#ifdef PLATFORM_POSIX - pthread_t handle; - if(pthread_create(&handle, 0, fn, ptr) == 0) { - pthread_detach(handle); - return true; - } -#endif - return false; -} - -IpAddrInfo::Entry IpAddrInfo::pool[COUNT]; - -RawMutex IpAddrInfoPoolMutex; - -void IpAddrInfo::EnterPool() -{ - IpAddrInfoPoolMutex.Enter(); -} - -void IpAddrInfo::LeavePool() -{ - IpAddrInfoPoolMutex.Leave(); -} - -int sGetAddrInfo(const char *host, const char *port, addrinfo **result) -{ - addrinfo hints; - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - return getaddrinfo(host, port, &hints, result); -} - -rawthread_t rawthread__ IpAddrInfo::Thread(void *ptr) -{ - Entry *entry = (Entry *)ptr; - EnterPool(); - if(entry->status == WORKING) { - char host[1025]; - char port[257]; - strcpy(host, entry->host); - strcpy(port, entry->port); - LeavePool(); - addrinfo *result; - if(sGetAddrInfo(host, port, &result) == 0 && result) { - EnterPool(); - if(entry->status == WORKING) { - entry->addr = result; - entry->status = RESOLVED; - } - else { - freeaddrinfo(entry->addr); - entry->status = EMPTY; - } - } - else { - EnterPool(); - if(entry->status == CANCELED) - entry->status = EMPTY; - else - entry->status = FAILED; - } - } - LeavePool(); - return 0; -} - -bool IpAddrInfo::Execute(const String& host, int port) -{ - Clear(); - entry = exe; - addrinfo *result; - entry->addr = sGetAddrInfo(~host, ~AsString(port), &result) == 0 ? result : NULL; - return entry->addr; -} - -void IpAddrInfo::Start() -{ - if(entry) - return; - EnterPool(); - for(int i = 0; i < COUNT; i++) { - Entry *e = pool + i; - if(e->status == EMPTY) { - entry = e; - e->addr = NULL; - if(host.GetCount() > 1024 || port.GetCount() > 256) - e->status = FAILED; - else { - e->status = WORKING; - e->host = host; - e->port = port; - StartRawThread(&IpAddrInfo::Thread, e); - } - break; - } - } - LeavePool(); -} - -void IpAddrInfo::Start(const String& host_, int port_) -{ - Clear(); - port = AsString(port_); - host = host_; - Start(); -} - -bool IpAddrInfo::InProgress() -{ - if(!entry) { - Start(); - return true; - } - EnterPool(); - int s = entry->status; - LeavePool(); - return s == WORKING; -} - -addrinfo *IpAddrInfo::GetResult() -{ - EnterPool(); - addrinfo *ai = entry ? entry->addr : NULL; - LeavePool(); - return ai; -} - -void IpAddrInfo::Clear() -{ - EnterPool(); - if(entry) { - if(entry->status == RESOLVED && entry->addr) - freeaddrinfo(entry->addr); - if(entry->status == WORKING) - entry->status = CANCELED; - entry->status = EMPTY; - entry = NULL; - } - LeavePool(); -} - -IpAddrInfo::IpAddrInfo() -{ - TcpSocket::Init(); - entry = NULL; -} - -#ifdef PLATFORM_POSIX - -#define SOCKERR(x) x - -const char *TcpSocketErrorDesc(int code) -{ - return strerror(code); -} - -int TcpSocket::GetErrorCode() -{ - return errno; -} - -#else - -#define SOCKERR(x) WSA##x - -const char *TcpSocketErrorDesc(int code) -{ - static Tuple2 err[] = { - { WSAEINTR, "Interrupted function call." }, - { WSAEACCES, "Permission denied." }, - { WSAEFAULT, "Bad address." }, - { WSAEINVAL, "Invalid argument." }, - { WSAEMFILE, "Too many open files." }, - { WSAEWOULDBLOCK, "Resource temporarily unavailable." }, - { WSAEINPROGRESS, "Operation now in progress." }, - { WSAEALREADY, "Operation already in progress." }, - { WSAENOTSOCK, "TcpSocket operation on nonsocket." }, - { WSAEDESTADDRREQ, "Destination address required." }, - { WSAEMSGSIZE, "Message too long." }, - { WSAEPROTOTYPE, "Protocol wrong type for socket." }, - { WSAENOPROTOOPT, "Bad protocol option." }, - { WSAEPROTONOSUPPORT, "Protocol not supported." }, - { WSAESOCKTNOSUPPORT, "TcpSocket type not supported." }, - { WSAEOPNOTSUPP, "Operation not supported." }, - { WSAEPFNOSUPPORT, "Protocol family not supported." }, - { WSAEAFNOSUPPORT, "Address family not supported by protocol family." }, - { WSAEADDRINUSE, "Address already in use." }, - { WSAEADDRNOTAVAIL, "Cannot assign requested address." }, - { WSAENETDOWN, "Network is down." }, - { WSAENETUNREACH, "Network is unreachable." }, - { WSAENETRESET, "Network dropped connection on reset." }, - { WSAECONNABORTED, "Software caused connection abort." }, - { WSAECONNRESET, "Connection reset by peer." }, - { WSAENOBUFS, "No buffer space available." }, - { WSAEISCONN, "TcpSocket is already connected." }, - { WSAENOTCONN, "TcpSocket is not connected." }, - { WSAESHUTDOWN, "Cannot send after socket shutdown." }, - { WSAETIMEDOUT, "Connection timed out." }, - { WSAECONNREFUSED, "Connection refused." }, - { WSAEHOSTDOWN, "Host is down." }, - { WSAEHOSTUNREACH, "No route to host." }, - { WSAEPROCLIM, "Too many processes." }, - { WSASYSNOTREADY, "Network subsystem is unavailable." }, - { WSAVERNOTSUPPORTED, "Winsock.dll version out of range." }, - { WSANOTINITIALISED, "Successful WSAStartup not yet performed." }, - { WSAEDISCON, "Graceful shutdown in progress." }, - { WSATYPE_NOT_FOUND, "Class type not found." }, - { WSAHOST_NOT_FOUND, "Host not found." }, - { WSATRY_AGAIN, "Nonauthoritative host not found." }, - { WSANO_RECOVERY, "This is a nonrecoverable error." }, - { WSANO_DATA, "Valid name, no data record of requested type." }, - { WSASYSCALLFAILURE, "System call failure." }, - }; - const Tuple2 *x = FindTuple(err, __countof(err), code); - return x ? x->b : "Unknown error code."; -} - -int TcpSocket::GetErrorCode() -{ - return WSAGetLastError(); -} - -#endif - - -void TcpSocket::Init() -{ -#if defined(PLATFORM_WIN32) - ONCELOCK { - WSADATA wsadata; - WSAStartup(MAKEWORD(2, 2), &wsadata); - } -#endif -} - -void TcpSocket::Reset() -{ - is_eof = false; - socket = INVALID_SOCKET; - ipv6 = false; - ptr = end = buffer; - is_error = false; - is_abort = false; -} - -TcpSocket::TcpSocket() -{ - ClearError(); - Reset(); - timeout = Null; - waitstep = 20; -} - -bool TcpSocket::Open(int family, int type, int protocol) -{ - Init(); - Close(); - ClearError(); - if((socket = ::socket(family, type, protocol)) == INVALID_SOCKET) - return false; - LLOG("TcpSocket::Data::Open() -> " << (int)socket); -#ifdef PLATFORM_WIN32 - u_long arg = 1; - if(ioctlsocket(socket, FIONBIO, &arg)) - SetSockError("ioctlsocket(FIO[N]BIO)"); -#else - if(fcntl(socket, F_SETFL, (fcntl(socket, F_GETFL, 0) | O_NONBLOCK))) - SetSockError("fcntl(O_[NON]BLOCK)"); -#endif - return true; -} - -bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse) -{ - Init(); - - ipv6 = ipv6_; - if(!Open(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) - return false; - sockaddr_in sin; -#ifdef PLATFORM_WIN32 - SOCKADDR_IN6 sin6; - if(ipv6 && IsWinVista()) -#else - sockaddr_in6 sin6; - if(ipv6) -#endif - { - Zero(sin6); - sin.sin_family = AF_INET6; - sin.sin_port = htons(port); - sin.sin_addr.s_addr = htonl(INADDR_ANY); - } - else { - Zero(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - sin.sin_addr.s_addr = htonl(INADDR_ANY); - } - if(reuse) { - int optval = 1; - setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)); - } - if(bind(socket, ipv6 ? (const sockaddr *)&sin6 : (const sockaddr *)&sin, - ipv6 ? sizeof(sin6) : sizeof(sin))) { - SetSockError(Format("bind(port=%d)", port)); - return false; - } - if(listen(socket, listen_count)) { - SetSockError(Format("listen(port=%d, count=%d)", port, listen_count)); - return false; - } - return true; -} - -bool TcpSocket::Accept(TcpSocket& ls) -{ - Close(); - if(timeout && !ls.WaitRead()) - return false; - if(!Open(ls.ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) - return false; - socket = accept(ls.GetSOCKET(), NULL, NULL); - if(socket == INVALID_SOCKET) { - SetSockError("accept"); - return false; - } - return true; -} - -String TcpSocket::GetPeerAddr() const -{ - if(!IsOpen()) - return Null; - sockaddr_in addr; - socklen_t l = sizeof(addr); - if(getpeername(socket, (sockaddr *)&addr, &l) != 0) - return Null; - if(l > sizeof(addr)) - return Null; -#ifdef PLATFORM_WIN32 - return inet_ntoa(addr.sin_addr); -#else - char h[200]; - return inet_ntop(AF_INET, &addr.sin_addr, h, 200); -#endif -} - -void TcpSocket::NoDelay() -{ - ASSERT(IsOpen()); - int __true = 1; - LLOG("NoDelay(" << (int)socket << ")"); - if(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char *)&__true, sizeof(__true))) - SetSockError("setsockopt(TCP_NODELAY)"); -} - -void TcpSocket::Linger(int msecs) -{ - ASSERT(IsOpen()); - linger ls; - ls.l_onoff = !IsNull(msecs) ? 1 : 0; - ls.l_linger = !IsNull(msecs) ? (msecs + 999) / 1000 : 0; - if(setsockopt(socket, SOL_SOCKET, SO_LINGER, - reinterpret_cast(&ls), sizeof(ls))) - SetSockError("setsockopt(SO_LINGER)"); -} - -void TcpSocket::Attach(SOCKET s) -{ - Close(); - socket = s; -} - -bool TcpSocket::RawConnect(addrinfo *rp) -{ - for(;;) { - if(Open(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) { - if(connect(socket, rp->ai_addr, rp->ai_addrlen) == 0 || - GetErrorCode() == SOCKERR(EINPROGRESS) || GetErrorCode() == SOCKERR(EWOULDBLOCK)) - break; - Close(); - } - rp = rp->ai_next; - if(!rp) { - SetSockError("connect has failed"); - return false; - } - } - return true; -} - - -bool TcpSocket::Connect(IpAddrInfo& info) -{ - LLOG("TCP Connect addrinfo"); - Init(); - addrinfo *result = info.GetResult(); - return result && RawConnect(result); -} - -bool TcpSocket::Connect(const char *host, int port) -{ - LLOG("TCP Connect(" << host << ':' << port << ')'); - - Init(); - IpAddrInfo info; - if(!info.Execute(host, port)) { - SetSockError(Format("getaddrinfo(%s) failed", host)); - return false; - } - return Connect(info); -} - -void TcpSocket::Close() -{ - LLOG("TCP close " << (int)socket); - if(socket != INVALID_SOCKET) { - int res; -#if defined(PLATFORM_WIN32) - res = closesocket(socket); -#elif defined(PLATFORM_POSIX) - res = close(socket); -#else - #error Unsupported platform -#endif - if(res && !IsError()) - SetSockError("close"); - socket = INVALID_SOCKET; - } -} - -bool TcpSocket::WouldBlock() -{ - int c = GetErrorCode(); -#ifdef PLATFORM_POSIX - return c == SOCKERR(EWOULDBLOCK) || c == SOCKERR(EAGAIN); -#endif -#ifdef PLATFORM_WIN32 - return c == SOCKERR(EWOULDBLOCK) || c == SOCKERR(ENOTCONN); -#endif -} - -int TcpSocket::Send(const void *buf, int amount) -{ - int res = send(socket, (const char *)buf, amount, 0); - if(res < 0 && WouldBlock()) - res = 0; - else - if(res == 0 || res < 0) - SetSockError("send"); - return res; -} - -void TcpSocket::Shutdown() -{ - ASSERT(IsOpen()); - if(shutdown(socket, SD_SEND)) - SetSockError("shutdown(SD_SEND)"); -} - -String TcpSocket::GetHostName() -{ - Init(); - char buffer[256]; - gethostname(buffer, __countof(buffer)); - return buffer; -} - -bool TcpSocket::Wait(dword flags) -{ - LLOG("Wait(" << timeout << ", " << flags << ")"); - if((flags & WAIT_READ) && ptr != end) - return true; - int end_time = msecs() + timeout; - if(socket == INVALID_SOCKET) - return false; - for(;;) { - if(IsError() || IsAbort()) - return false; - int to = end_time - msecs(); - if(WhenWait) - to = waitstep; - timeval *tvalp = NULL; - timeval tval; - if(!IsNull(timeout) || WhenWait) { - to = max(to, 0); - tval.tv_sec = to / 1000; - tval.tv_usec = 1000 * (to % 1000); - tvalp = &tval; - } - fd_set fdset[1]; - FD_ZERO(fdset); - FD_SET(socket, fdset); - int avail = select((int)socket + 1, - flags & WAIT_READ ? fdset : NULL, - flags & WAIT_WRITE ? fdset : NULL, - flags & WAIT_EXCEPTION ? fdset : NULL, tvalp); - LLOG("Wait select avail: " << avail); - if(avail < 0) { - SetSockError("wait"); - return false; - } - if(avail > 0) - return true; - if(to <= 0 && timeout) { - return false; - } - WhenWait(); - if(timeout == 0) - return false; - } -} - -int TcpSocket::Put(const char *s, int length) -{ - LLOG("Put " << socket << ": " << length); - ASSERT(IsOpen()); - if(length < 0 && s) - length = (int)strlen(s); - if(!s || length <= 0 || IsError() || IsAbort()) - return 0; - done = 0; - bool peek = false; - while(done < length) { - if(peek && !WaitWrite()) - return done; - peek = false; - int count = Send(s + done, length - done); - if(IsError() || timeout == 0 && count == 0 && peek) - return done; - if(count > 0) - done += count; - else - peek = true; - } - LLOG("//Put() -> " << done); - return done; -} - -int TcpSocket::Recv(void *buf, int amount) -{ - int res = recv(socket, (char *)buf, amount, 0); - if(res == 0) - is_eof = true; - else - if(res < 0 && WouldBlock()) - res = 0; - else - if(res < 0) - SetSockError("recv"); - LLOG("recv(" << socket << "): " << res << " bytes: " - << AsCString((char *)buf, (char *)buf + min(res, 16)) - << (res ? "" : IsEof() ? ", EOF" : ", WOULDBLOCK")); - return res; -} - -void TcpSocket::ReadBuffer() -{ - ptr = end = buffer; - if(WaitRead()) - end = buffer + Recv(buffer, BUFFERSIZE); -} - -int TcpSocket::Get_() -{ - if(!IsOpen() || IsError() || IsEof() || IsAbort()) - return -1; - ReadBuffer(); - return ptr < end ? *ptr++ : -1; -} - -int TcpSocket::Peek_() -{ - if(!IsOpen() || IsError() || IsEof() || IsAbort()) - return -1; - ReadBuffer(); - return ptr < end ? *ptr : -1; -} - -int TcpSocket::Get(void *buffer, int count) -{ - LLOG("Get " << count); - - if(!IsOpen() || IsError() || IsEof() || IsAbort()) - return 0; - - String out; - int l = end - ptr; - done = 0; - if(l > 0) - if(l < count) { - memcpy(buffer, ptr, l); - done += l; - ptr = end; - } - else { - memcpy(buffer, ptr, count); - ptr += count; - return count; - } - while(done < count && !IsError() && !IsEof()) { - if(!WaitRead()) - break; - int part = Recv((char *)buffer + done, count - done); - if(part > 0) - done += part; - if(timeout == 0) - break; - } - return done; -} - -String TcpSocket::Get(int count) -{ - if(count == 0) - return Null; - StringBuffer out(count); - int done = Get(out, count); - if(!done && IsEof()) - return String::GetVoid(); - out.SetLength(done); - return out; -} - -String TcpSocket::GetLine(int maxlen) -{ - String ln; - for(;;) { - int c = Peek(); - if(c < 0) - return String::GetVoid(); - Get(); - if(c == '\n') - return ln; - if(c != '\r') - ln.Cat(c); - } -} - -void TcpSocket::SetSockError(const char *context, const char *errdesc) -{ - String err; - errorcode = GetErrorCode(); - if(socket != INVALID_SOCKET) - err << "socket(" << (int)socket << ") / "; - err << context << ": " << errdesc; - errordesc = err; - is_error = true; -} - -void TcpSocket::SetSockError(const char *context) -{ - SetSockError(context, TcpSocketErrorDesc(GetErrorCode())); -} - -int SocketWaitEvent::Wait(int timeout) -{ - FD_ZERO(read); - FD_ZERO(write); - FD_ZERO(exception); - int maxindex = -1; - for(int i = 0; i < socket.GetCount(); i++) { - const Tuple2& s = socket[i]; - if(s.a >= 0) { - const Tuple2& s = socket[i]; - if(s.b & WAIT_READ) - FD_SET(s.a, read); - if(s.b & WAIT_WRITE) - FD_SET(s.a, write); - if(s.b & WAIT_EXCEPTION) - FD_SET(s.a, exception); - maxindex = max(s.a, maxindex); - } - } - timeval *tvalp = NULL; - timeval tval; - if(!IsNull(timeout)) { - tval.tv_sec = timeout / 1000; - tval.tv_usec = 1000 * (timeout % 1000); - tvalp = &tval; - } - return select(maxindex + 1, read, write, exception, tvalp); -} - -dword SocketWaitEvent::Get(int i) const -{ - int s = socket[i].a; - if(s < 0) - return 0; - dword events = 0; - if(FD_ISSET(s, read)) - events |= WAIT_READ; - if(FD_ISSET(s, write)) - events |= WAIT_WRITE; - if(FD_ISSET(s, exception)) - events |= WAIT_EXCEPTION; - return events; -} - -SocketWaitEvent::SocketWaitEvent() -{ - FD_ZERO(read); - FD_ZERO(write); - FD_ZERO(exception); -} - -END_UPP_NAMESPACE +#include "Core.h" + +#ifdef PLATFORM_WIN32 +#include +#include +#include +#endif + +#ifdef PLATFORM_POSIX +#include +#endif + +NAMESPACE_UPP + +#ifdef PLATFORM_WIN32 +#pragma comment(lib, "ws2_32.lib") +#endif + +#define LLOG(x) // DLOG("TCP " << x) + +IpAddrInfo::Entry IpAddrInfo::pool[COUNT]; + +RawMutex IpAddrInfoPoolMutex; + +void IpAddrInfo::EnterPool() +{ + IpAddrInfoPoolMutex.Enter(); +} + +void IpAddrInfo::LeavePool() +{ + IpAddrInfoPoolMutex.Leave(); +} + +int sGetAddrInfo(const char *host, const char *port, addrinfo **result) +{ + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + return getaddrinfo(host, port, &hints, result); +} + +rawthread_t rawthread__ IpAddrInfo::Thread(void *ptr) +{ + Entry *entry = (Entry *)ptr; + EnterPool(); + if(entry->status == WORKING) { + char host[1025]; + char port[257]; + strcpy(host, entry->host); + strcpy(port, entry->port); + LeavePool(); + addrinfo *result; + if(sGetAddrInfo(host, port, &result) == 0 && result) { + EnterPool(); + if(entry->status == WORKING) { + entry->addr = result; + entry->status = RESOLVED; + } + else { + freeaddrinfo(entry->addr); + entry->status = EMPTY; + } + } + else { + EnterPool(); + if(entry->status == CANCELED) + entry->status = EMPTY; + else + entry->status = FAILED; + } + } + LeavePool(); + return 0; +} + +bool IpAddrInfo::Execute(const String& host, int port) +{ + Clear(); + entry = exe; + addrinfo *result; + entry->addr = sGetAddrInfo(~host, ~AsString(port), &result) == 0 ? result : NULL; + return entry->addr; +} + +void IpAddrInfo::Start() +{ + if(entry) + return; + EnterPool(); + for(int i = 0; i < COUNT; i++) { + Entry *e = pool + i; + if(e->status == EMPTY) { + entry = e; + e->addr = NULL; + if(host.GetCount() > 1024 || port.GetCount() > 256) + e->status = FAILED; + else { + e->status = WORKING; + e->host = host; + e->port = port; + StartRawThread(&IpAddrInfo::Thread, e); + } + break; + } + } + LeavePool(); +} + +void IpAddrInfo::Start(const String& host_, int port_) +{ + Clear(); + port = AsString(port_); + host = host_; + Start(); +} + +bool IpAddrInfo::InProgress() +{ + if(!entry) { + Start(); + return true; + } + EnterPool(); + int s = entry->status; + LeavePool(); + return s == WORKING; +} + +addrinfo *IpAddrInfo::GetResult() +{ + EnterPool(); + addrinfo *ai = entry ? entry->addr : NULL; + LeavePool(); + return ai; +} + +void IpAddrInfo::Clear() +{ + EnterPool(); + if(entry) { + if(entry->status == RESOLVED && entry->addr) + freeaddrinfo(entry->addr); + if(entry->status == WORKING) + entry->status = CANCELED; + entry->status = EMPTY; + entry = NULL; + } + LeavePool(); +} + +IpAddrInfo::IpAddrInfo() +{ + TcpSocket::Init(); + entry = NULL; +} + +#ifdef PLATFORM_POSIX + +#define SOCKERR(x) x + +const char *TcpSocketErrorDesc(int code) +{ + return strerror(code); +} + +int TcpSocket::GetErrorCode() +{ + return errno; +} + +#else + +#define SOCKERR(x) WSA##x + +const char *TcpSocketErrorDesc(int code) +{ + static Tuple2 err[] = { + { WSAEINTR, "Interrupted function call." }, + { WSAEACCES, "Permission denied." }, + { WSAEFAULT, "Bad address." }, + { WSAEINVAL, "Invalid argument." }, + { WSAEMFILE, "Too many open files." }, + { WSAEWOULDBLOCK, "Resource temporarily unavailable." }, + { WSAEINPROGRESS, "Operation now in progress." }, + { WSAEALREADY, "Operation already in progress." }, + { WSAENOTSOCK, "TcpSocket operation on nonsocket." }, + { WSAEDESTADDRREQ, "Destination address required." }, + { WSAEMSGSIZE, "Message too long." }, + { WSAEPROTOTYPE, "Protocol wrong type for socket." }, + { WSAENOPROTOOPT, "Bad protocol option." }, + { WSAEPROTONOSUPPORT, "Protocol not supported." }, + { WSAESOCKTNOSUPPORT, "TcpSocket type not supported." }, + { WSAEOPNOTSUPP, "Operation not supported." }, + { WSAEPFNOSUPPORT, "Protocol family not supported." }, + { WSAEAFNOSUPPORT, "Address family not supported by protocol family." }, + { WSAEADDRINUSE, "Address already in use." }, + { WSAEADDRNOTAVAIL, "Cannot assign requested address." }, + { WSAENETDOWN, "Network is down." }, + { WSAENETUNREACH, "Network is unreachable." }, + { WSAENETRESET, "Network dropped connection on reset." }, + { WSAECONNABORTED, "Software caused connection abort." }, + { WSAECONNRESET, "Connection reset by peer." }, + { WSAENOBUFS, "No buffer space available." }, + { WSAEISCONN, "TcpSocket is already connected." }, + { WSAENOTCONN, "TcpSocket is not connected." }, + { WSAESHUTDOWN, "Cannot send after socket shutdown." }, + { WSAETIMEDOUT, "Connection timed out." }, + { WSAECONNREFUSED, "Connection refused." }, + { WSAEHOSTDOWN, "Host is down." }, + { WSAEHOSTUNREACH, "No route to host." }, + { WSAEPROCLIM, "Too many processes." }, + { WSASYSNOTREADY, "Network subsystem is unavailable." }, + { WSAVERNOTSUPPORTED, "Winsock.dll version out of range." }, + { WSANOTINITIALISED, "Successful WSAStartup not yet performed." }, + { WSAEDISCON, "Graceful shutdown in progress." }, + { WSATYPE_NOT_FOUND, "Class type not found." }, + { WSAHOST_NOT_FOUND, "Host not found." }, + { WSATRY_AGAIN, "Nonauthoritative host not found." }, + { WSANO_RECOVERY, "This is a nonrecoverable error." }, + { WSANO_DATA, "Valid name, no data record of requested type." }, + { WSASYSCALLFAILURE, "System call failure." }, + }; + const Tuple2 *x = FindTuple(err, __countof(err), code); + return x ? x->b : "Unknown error code."; +} + +int TcpSocket::GetErrorCode() +{ + return WSAGetLastError(); +} + +#endif + + +void TcpSocket::Init() +{ +#if defined(PLATFORM_WIN32) + ONCELOCK { + WSADATA wsadata; + WSAStartup(MAKEWORD(2, 2), &wsadata); + } +#endif +} + +void TcpSocket::Reset() +{ + is_eof = false; + socket = INVALID_SOCKET; + ipv6 = false; + ptr = end = buffer; + is_error = false; + is_abort = false; +} + +TcpSocket::TcpSocket() +{ + ClearError(); + Reset(); + timeout = Null; + waitstep = 20; +} + +bool TcpSocket::Open(int family, int type, int protocol) +{ + Init(); + Close(); + ClearError(); + if((socket = ::socket(family, type, protocol)) == INVALID_SOCKET) + return false; + LLOG("TcpSocket::Data::Open() -> " << (int)socket); +#ifdef PLATFORM_WIN32 + u_long arg = 1; + if(ioctlsocket(socket, FIONBIO, &arg)) + SetSockError("ioctlsocket(FIO[N]BIO)"); +#else + if(fcntl(socket, F_SETFL, (fcntl(socket, F_GETFL, 0) | O_NONBLOCK))) + SetSockError("fcntl(O_[NON]BLOCK)"); +#endif + return true; +} + +bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse) +{ + Init(); + + ipv6 = ipv6_; + if(!Open(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) + return false; + sockaddr_in sin; +#ifdef PLATFORM_WIN32 + SOCKADDR_IN6 sin6; + if(ipv6 && IsWinVista()) +#else + sockaddr_in6 sin6; + if(ipv6) +#endif + { + Zero(sin6); + sin.sin_family = AF_INET6; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + Zero(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + if(reuse) { + int optval = 1; + setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)); + } + if(bind(socket, ipv6 ? (const sockaddr *)&sin6 : (const sockaddr *)&sin, + ipv6 ? sizeof(sin6) : sizeof(sin))) { + SetSockError(Format("bind(port=%d)", port)); + return false; + } + if(listen(socket, listen_count)) { + SetSockError(Format("listen(port=%d, count=%d)", port, listen_count)); + return false; + } + return true; +} + +bool TcpSocket::Accept(TcpSocket& ls) +{ + Close(); + if(timeout && !ls.WaitRead()) + return false; + if(!Open(ls.ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) + return false; + socket = accept(ls.GetSOCKET(), NULL, NULL); + if(socket == INVALID_SOCKET) { + SetSockError("accept"); + return false; + } + return true; +} + +String TcpSocket::GetPeerAddr() const +{ + if(!IsOpen()) + return Null; + sockaddr_in addr; + socklen_t l = sizeof(addr); + if(getpeername(socket, (sockaddr *)&addr, &l) != 0) + return Null; + if(l > sizeof(addr)) + return Null; +#ifdef PLATFORM_WIN32 + return inet_ntoa(addr.sin_addr); +#else + char h[200]; + return inet_ntop(AF_INET, &addr.sin_addr, h, 200); +#endif +} + +void TcpSocket::NoDelay() +{ + ASSERT(IsOpen()); + int __true = 1; + LLOG("NoDelay(" << (int)socket << ")"); + if(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char *)&__true, sizeof(__true))) + SetSockError("setsockopt(TCP_NODELAY)"); +} + +void TcpSocket::Linger(int msecs) +{ + ASSERT(IsOpen()); + linger ls; + ls.l_onoff = !IsNull(msecs) ? 1 : 0; + ls.l_linger = !IsNull(msecs) ? (msecs + 999) / 1000 : 0; + if(setsockopt(socket, SOL_SOCKET, SO_LINGER, + reinterpret_cast(&ls), sizeof(ls))) + SetSockError("setsockopt(SO_LINGER)"); +} + +void TcpSocket::Attach(SOCKET s) +{ + Close(); + socket = s; +} + +bool TcpSocket::RawConnect(addrinfo *rp) +{ + for(;;) { + if(Open(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) { + if(connect(socket, rp->ai_addr, rp->ai_addrlen) == 0 || + GetErrorCode() == SOCKERR(EINPROGRESS) || GetErrorCode() == SOCKERR(EWOULDBLOCK)) + break; + Close(); + } + rp = rp->ai_next; + if(!rp) { + SetSockError("connect has failed"); + return false; + } + } + return true; +} + + +bool TcpSocket::Connect(IpAddrInfo& info) +{ + LLOG("TCP Connect addrinfo"); + Init(); + addrinfo *result = info.GetResult(); + return result && RawConnect(result); +} + +bool TcpSocket::Connect(const char *host, int port) +{ + LLOG("TCP Connect(" << host << ':' << port << ')'); + + Init(); + IpAddrInfo info; + if(!info.Execute(host, port)) { + SetSockError(Format("getaddrinfo(%s) failed", host)); + return false; + } + return Connect(info); +} + +void TcpSocket::Close() +{ + LLOG("TCP close " << (int)socket); + if(socket != INVALID_SOCKET) { + int res; +#if defined(PLATFORM_WIN32) + res = closesocket(socket); +#elif defined(PLATFORM_POSIX) + res = close(socket); +#else + #error Unsupported platform +#endif + if(res && !IsError()) + SetSockError("close"); + socket = INVALID_SOCKET; + } +} + +bool TcpSocket::WouldBlock() +{ + int c = GetErrorCode(); +#ifdef PLATFORM_POSIX + return c == SOCKERR(EWOULDBLOCK) || c == SOCKERR(EAGAIN); +#endif +#ifdef PLATFORM_WIN32 + return c == SOCKERR(EWOULDBLOCK) || c == SOCKERR(ENOTCONN); +#endif +} + +int TcpSocket::Send(const void *buf, int amount) +{ + int res = send(socket, (const char *)buf, amount, 0); + if(res < 0 && WouldBlock()) + res = 0; + else + if(res == 0 || res < 0) + SetSockError("send"); + return res; +} + +void TcpSocket::Shutdown() +{ + ASSERT(IsOpen()); + if(shutdown(socket, SD_SEND)) + SetSockError("shutdown(SD_SEND)"); +} + +String TcpSocket::GetHostName() +{ + Init(); + char buffer[256]; + gethostname(buffer, __countof(buffer)); + return buffer; +} + +bool TcpSocket::Wait(dword flags) +{ + LLOG("Wait(" << timeout << ", " << flags << ")"); + if((flags & WAIT_READ) && ptr != end) + return true; + int end_time = msecs() + timeout; + if(socket == INVALID_SOCKET) + return false; + for(;;) { + if(IsError() || IsAbort()) + return false; + int to = end_time - msecs(); + if(WhenWait) + to = waitstep; + timeval *tvalp = NULL; + timeval tval; + if(!IsNull(timeout) || WhenWait) { + to = max(to, 0); + tval.tv_sec = to / 1000; + tval.tv_usec = 1000 * (to % 1000); + tvalp = &tval; + } + fd_set fdset[1]; + FD_ZERO(fdset); + FD_SET(socket, fdset); + int avail = select((int)socket + 1, + flags & WAIT_READ ? fdset : NULL, + flags & WAIT_WRITE ? fdset : NULL, + flags & WAIT_EXCEPTION ? fdset : NULL, tvalp); + LLOG("Wait select avail: " << avail); + if(avail < 0) { + SetSockError("wait"); + return false; + } + if(avail > 0) + return true; + if(to <= 0 && timeout) { + return false; + } + WhenWait(); + if(timeout == 0) + return false; + } +} + +int TcpSocket::Put(const char *s, int length) +{ + LLOG("Put " << socket << ": " << length); + ASSERT(IsOpen()); + if(length < 0 && s) + length = (int)strlen(s); + if(!s || length <= 0 || IsError() || IsAbort()) + return 0; + done = 0; + bool peek = false; + while(done < length) { + if(peek && !WaitWrite()) + return done; + peek = false; + int count = Send(s + done, length - done); + if(IsError() || timeout == 0 && count == 0 && peek) + return done; + if(count > 0) + done += count; + else + peek = true; + } + LLOG("//Put() -> " << done); + return done; +} + +int TcpSocket::Recv(void *buf, int amount) +{ + int res = recv(socket, (char *)buf, amount, 0); + if(res == 0) + is_eof = true; + else + if(res < 0 && WouldBlock()) + res = 0; + else + if(res < 0) + SetSockError("recv"); + LLOG("recv(" << socket << "): " << res << " bytes: " + << AsCString((char *)buf, (char *)buf + min(res, 16)) + << (res ? "" : IsEof() ? ", EOF" : ", WOULDBLOCK")); + return res; +} + +void TcpSocket::ReadBuffer() +{ + ptr = end = buffer; + if(WaitRead()) + end = buffer + Recv(buffer, BUFFERSIZE); +} + +int TcpSocket::Get_() +{ + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return -1; + ReadBuffer(); + return ptr < end ? *ptr++ : -1; +} + +int TcpSocket::Peek_() +{ + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return -1; + ReadBuffer(); + return ptr < end ? *ptr : -1; +} + +int TcpSocket::Get(void *buffer, int count) +{ + LLOG("Get " << count); + + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return 0; + + String out; + int l = end - ptr; + done = 0; + if(l > 0) + if(l < count) { + memcpy(buffer, ptr, l); + done += l; + ptr = end; + } + else { + memcpy(buffer, ptr, count); + ptr += count; + return count; + } + while(done < count && !IsError() && !IsEof()) { + if(!WaitRead()) + break; + int part = Recv((char *)buffer + done, count - done); + if(part > 0) + done += part; + if(timeout == 0) + break; + } + return done; +} + +String TcpSocket::Get(int count) +{ + if(count == 0) + return Null; + StringBuffer out(count); + int done = Get(out, count); + if(!done && IsEof()) + return String::GetVoid(); + out.SetLength(done); + return out; +} + +String TcpSocket::GetLine(int maxlen) +{ + String ln; + for(;;) { + int c = Peek(); + if(c < 0) + return String::GetVoid(); + Get(); + if(c == '\n') + return ln; + if(c != '\r') + ln.Cat(c); + } +} + +void TcpSocket::SetSockError(const char *context, const char *errdesc) +{ + String err; + errorcode = GetErrorCode(); + if(socket != INVALID_SOCKET) + err << "socket(" << (int)socket << ") / "; + err << context << ": " << errdesc; + errordesc = err; + is_error = true; +} + +void TcpSocket::SetSockError(const char *context) +{ + SetSockError(context, TcpSocketErrorDesc(GetErrorCode())); +} + +int SocketWaitEvent::Wait(int timeout) +{ + FD_ZERO(read); + FD_ZERO(write); + FD_ZERO(exception); + int maxindex = -1; + for(int i = 0; i < socket.GetCount(); i++) { + const Tuple2& s = socket[i]; + if(s.a >= 0) { + const Tuple2& s = socket[i]; + if(s.b & WAIT_READ) + FD_SET(s.a, read); + if(s.b & WAIT_WRITE) + FD_SET(s.a, write); + if(s.b & WAIT_EXCEPTION) + FD_SET(s.a, exception); + maxindex = max(s.a, maxindex); + } + } + timeval *tvalp = NULL; + timeval tval; + if(!IsNull(timeout)) { + tval.tv_sec = timeout / 1000; + tval.tv_usec = 1000 * (timeout % 1000); + tvalp = &tval; + } + return select(maxindex + 1, read, write, exception, tvalp); +} + +dword SocketWaitEvent::Get(int i) const +{ + int s = socket[i].a; + if(s < 0) + return 0; + dword events = 0; + if(FD_ISSET(s, read)) + events |= WAIT_READ; + if(FD_ISSET(s, write)) + events |= WAIT_WRITE; + if(FD_ISSET(s, exception)) + events |= WAIT_EXCEPTION; + return events; +} + +SocketWaitEvent::SocketWaitEvent() +{ + FD_ZERO(read); + FD_ZERO(write); + FD_ZERO(exception); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/Stream.cpp b/uppsrc/Core/Stream.cpp index cb53dc046..6f08db9eb 100644 --- a/uppsrc/Core/Stream.cpp +++ b/uppsrc/Core/Stream.cpp @@ -82,7 +82,7 @@ bool Stream::GetAll(void *data, dword size) { return true; } -String Stream::Get(dword size) +String Stream::Get(int size) { StringBuffer b(size); int n = Get(~b, size); @@ -417,8 +417,9 @@ void Stream::Put(Stream& s, int64 size, dword click) { } } -void Stream::SerializeRLE(byte *data, dword size) +void Stream::SerializeRLE(byte *data, int size) { + ASSERT(size >= 0); if(IsError()) return; byte *s = (byte *)data; byte *lim = s + size; @@ -465,7 +466,8 @@ void Stream::SerializeRLE(byte *data, dword size) } } -void Stream::SerializeRaw(byte *data, dword size) { +void Stream::SerializeRaw(byte *data, int size) { + ASSERT(size >= 0); if(IsError()) return; if(IsLoading()) GetAll(data, size); @@ -473,7 +475,8 @@ void Stream::SerializeRaw(byte *data, dword size) { Put(data, size); } -void Stream::SerializeRaw(word *data, dword count) { +void Stream::SerializeRaw(word *data, int count) { + ASSERT(count >= 0); #ifdef CPU_BE EndianSwap(data, count); #endif @@ -483,7 +486,8 @@ void Stream::SerializeRaw(word *data, dword count) { #endif } -void Stream::SerializeRaw(dword *data, dword count) { +void Stream::SerializeRaw(dword *data, int count) { + ASSERT(count >= 0); #ifdef CPU_BE EndianSwap(data, count); #endif @@ -493,7 +497,8 @@ void Stream::SerializeRaw(dword *data, dword count) { #endif } -void Stream::SerializeRaw(uint64 *data, dword count) { +void Stream::SerializeRaw(uint64 *data, int count) { + ASSERT(count >= 0); #ifdef CPU_BE EndianSwap(data, count); #endif diff --git a/uppsrc/Core/Stream.h b/uppsrc/Core/Stream.h index d31d4cfb9..d747e7ee0 100644 --- a/uppsrc/Core/Stream.h +++ b/uppsrc/Core/Stream.h @@ -80,16 +80,17 @@ public: void Put(int c) { if(ptr < wrlim) *ptr++ = c; else _Put(c); } int Term() { return ptr < rdlim ? *ptr : _Term(); } + int Peek() { return Term(); } int Get() { return ptr < rdlim ? *ptr++ : _Get(); } - const byte *Peek(int size = 1) { ASSERT(size > 0); return ptr + size <= rdlim ? ptr : NULL; } + const byte *PeekPtr(int size = 1){ ASSERT(size > 0); return ptr + size <= rdlim ? ptr : NULL; } byte *PutPtr(int size = 1) { ASSERT(size > 0); if(ptr + size <= wrlim) { byte *p = ptr; ptr += size; return p; }; return NULL; } - void Put(const void *data, dword size) { if(ptr + size <= wrlim) { memcpy(ptr, data, size); ptr += size; } else _Put(data, size); } - dword Get(void *data, dword size) { if(ptr + size <= rdlim) { memcpy(data, ptr, size); ptr += size; return size; } return _Get(data, size); } + void Put(const void *data, int size) { ASSERT(size >= 0); if(ptr + size <= wrlim) { memcpy(ptr, data, size); ptr += size; } else _Put(data, size); } + int Get(void *data, int size) { ASSERT(size >= 0); if(ptr + size <= rdlim) { memcpy(data, ptr, size); ptr += size; return size; } return _Get(data, size); } - void Put(const String& s) { Put((const char *) s, s.GetLength()); } - String Get(dword size); + void Put(const String& s) { Put((const char *) s, s.GetLength()); } + String Get(int size); void LoadThrowing() { style |= STRM_THROW; } void LoadError(); @@ -183,12 +184,12 @@ public: bool IsLoading() { return style & STRM_LOADING; } bool IsStoring() { return !IsLoading(); } - void SerializeRaw(byte *data, dword count); - void SerializeRaw(word *data, dword count); - void SerializeRaw(dword *data, dword count); - void SerializeRaw(uint64 *data, dword count); + void SerializeRaw(byte *data, int count); + void SerializeRaw(word *data, int count); + void SerializeRaw(dword *data, int count); + void SerializeRaw(uint64 *data, int count); - void SerializeRLE(byte *data, dword count); + void SerializeRLE(byte *data, int count); Stream& operator%(bool& d); Stream& operator%(char& d); diff --git a/uppsrc/Core/Web.h b/uppsrc/Core/Web.h index a2d045751..1b5e2b3bd 100644 --- a/uppsrc/Core/Web.h +++ b/uppsrc/Core/Web.h @@ -1,349 +1,349 @@ -String FormatIP(dword _ip); - -String UrlEncode(const String& s); -String UrlEncode(const String& s, const char *specials); -String UrlDecode(const char *b, const char *e); -inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); } - -String Base64Encode(const char *b, const char *e); -inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); } -String Base64Decode(const char *b, const char *e); -inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); } - -#ifdef PLATFORM_WIN32 -#define rawthread_t uintptr_t -#define rawthread__ __stdcall -#else -#define rawthread_t void * -#define rawthread__ -#endif - -class IpAddrInfo { - enum { COUNT = 32 }; - struct Entry { - const char *host; - const char *port; - int status; - addrinfo *addr; - }; - static Entry pool[COUNT]; - - enum { - EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED - }; - - String host, port; - Entry *entry; - Entry exe[1]; - - static void EnterPool(); - static void LeavePool(); - static rawthread_t rawthread__ Thread(void *ptr); - - void Start(); - -public: - void Start(const String& host, int port); - bool InProgress(); - bool Execute(const String& host, int port); - addrinfo *GetResult(); - void Clear(); - - IpAddrInfo(); - ~IpAddrInfo() { Clear(); } -}; - -enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 }; - -class TcpSocket { - enum { BUFFERSIZE = 512 }; - SOCKET socket; - char buffer[BUFFERSIZE]; - char *ptr; - char *end; - bool is_eof; - bool is_error; - bool is_abort; - bool ipv6; - - int timeout; - int waitstep; - int done; - - int errorcode; - String errordesc; - - - SOCKET AcceptRaw(dword *ipaddr, int timeout_msec); - bool Open(int family, int type, int protocol); - int Recv(void *buffer, int maxlen); - int Send(const void *buffer, int maxlen); - bool RawConnect(addrinfo *info); - - void ReadBuffer(); - int Get_(); - int Peek_(); - - void Reset(); - - void SetSockError(const char *context, const char *errdesc); - void SetSockError(const char *context); - - static int GetErrorCode(); - static bool WouldBlock(); - -public: - Callback WhenWait; - - static String GetHostName(); - - int GetDone() const { return done; } - - static void Init(); - - bool IsOpen() const { return socket != INVALID_SOCKET; } - bool IsEof() const { return is_eof && ptr == end; } - - bool IsError() const { return is_error; } - void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); } - int GetError() const { return errorcode; } - String GetErrorDesc() const { return errordesc; } - - void Abort() { is_abort = true; } - bool IsAbort() const { return is_abort; } - void ClearAbort() { is_abort = false; } - - SOCKET GetSOCKET() const { return socket; } - String GetPeerAddr() const; - - void Attach(SOCKET socket); - bool Connect(const char *host, int port); - bool Connect(IpAddrInfo& info); - bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true); - bool Accept(TcpSocket& listen_socket); - void Close(); - void Shutdown(); - - void NoDelay(); - void Linger(int msecs); - void NoLinger() { Linger(Null); } - void Reuse(bool reuse = true); - - bool Wait(dword events); - bool WaitRead() { return Wait(WAIT_READ); } - bool WaitWrite() { return Wait(WAIT_WRITE); } - - int Peek() { return ptr < end ? *ptr : Peek_(); } - int Term() { return Peek(); } - int Get() { return ptr < end ? *ptr++ : Get_(); } - int Get(void *buffer, int len); - String Get(int len); - int GetAll(void *buffer, int len) { return Get(buffer, len) == len; } - String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); } - String GetLine(int maxlen = 2000000); - - int Put(const char *s, int len); - int Put(const String& s) { return Put(s.Begin(), s.GetLength()); } - bool PutAll(const char *s, int len) { return Put(s, len) == len; } - bool PutAll(const String& s) { return Put(s) == s.GetCount(); } - - TcpSocket& Timeout(int ms) { timeout = ms; return *this; } - int GetTimeout() const { return timeout; } - TcpSocket& Blocking() { return Timeout(Null); } - - TcpSocket(); - ~TcpSocket() { Close(); } -}; - -class SocketWaitEvent { - Vector< Tuple2 > socket; - fd_set read[1], write[1], exception[1]; - -public: - void Clear() { socket.Clear(); } - void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); } - void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); } - int Wait(int timeout); - dword Get(int i) const; - dword operator[](int i) const { return Get(i); } - - SocketWaitEvent(); -}; - -struct HttpHeader { - String first_line; - VectorMap fields; - - String operator[](const char *id) { return fields.Get(id, Null); } - - bool Response(String& protocol, int& code, String& reason); - bool Request(String& method, String& uri, String& version); - - void Clear(); - bool Parse(const String& hdrs); -}; - -class HttpRequest : public TcpSocket { - int phase; - String data; - int count; - - HttpHeader header; - - String error; - String body; - - enum { - DEFAULT_HTTP_PORT = 80, - }; - - enum { - METHOD_GET, - METHOD_POST, - METHOD_HEAD, - METHOD_PUT, - }; - - int max_header_size; - int max_content_size; - int max_redirects; - int max_retries; - int timeout; - - String host; - int port; - String proxy_host; - int proxy_port; - String proxy_username; - String proxy_password; - String path; - - int method; - String accept; - String agent; - bool force_digest; - bool is_post; - bool std_headers; - bool hasurlvar; - String contenttype; - String username; - String password; - String digest; - String request_headers; - String postdata; - - String protocol; - int status_code; - String reason_phrase; - - int start_time; - int retry_count; - int redirect_count; - - int chunk; - - IpAddrInfo addrinfo; - int bodylen; - bool gzip; - Zlib z; - - void Init(); - - void StartPhase(int s); - void Start(); - void Dns(); - void StartRequest(); - bool SendingData(); - bool ReadingHeader(); - void StartBody(); - bool ReadingBody(); - void ReadingChunkHeader(); - void Finish(); - - void HttpError(const char *s); - void ContentOut(const void *ptr, dword size); - void Out(const void *ptr, dword size); - - String CalculateDigest(const String& authenticate) const; - -public: - Callback2 WhenContent; - - HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; } - HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; } - HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; } - HttpRequest& MaxRetries(int n) { max_retries = n; return *this; } - HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; } - HttpRequest& ChunkSize(int n) { chunk = n; return *this; } - - HttpRequest& Method(int m) { method = m; return *this; } - HttpRequest& GET() { return Method(METHOD_GET); } - HttpRequest& POST() { return Method(METHOD_POST); } - HttpRequest& HEAD() { return Method(METHOD_HEAD); } - HttpRequest& PUT() { return Method(METHOD_PUT); } - - HttpRequest& Host(const String& h) { host = h; return *this; } - HttpRequest& Port(int p) { port = p; return *this; } - HttpRequest& Path(const String& p) { path = p; return *this; } - HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; } - HttpRequest& Digest() { force_digest = true; return *this; } - HttpRequest& Digest(const String& d) { digest = d; return *this; } - HttpRequest& Url(const char *url); - HttpRequest& UrlVar(const char *id, const String& data); - HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); } - HttpRequest& PostData(const String& pd) { postdata = pd; return *this; } - HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); } - HttpRequest& Post(const String& data) { POST(); return PostData(data); } - HttpRequest& Post(const char *id, const String& data); - - HttpRequest& Headers(const String& h) { request_headers = h; return *this; } - HttpRequest& ClearHeaders() { return Headers(Null); } - HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; } - HttpRequest& Header(const char *id, const String& data); - - HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; } - HttpRequest& NoStdHeaders() { return StdHeaders(false); } - HttpRequest& Accept(const String& a) { accept = a; return *this; } - HttpRequest& Agent(const String& a) { agent = a; return *this; } - HttpRequest& ContentType(const String& a) { contenttype = a; return *this; } - - HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; } - HttpRequest& Proxy(const char *url); - HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; } - - bool IsSocketError() const { return TcpSocket::IsError(); } - bool IsHttpError() const { return !IsNull(error) ; } - bool IsError() const { return IsSocketError() || IsHttpError(); } - String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; } - void ClearError() { TcpSocket::ClearError(); error.Clear(); } - - String GetHeader(const char *s) { return header[s]; } - String operator[](const char *s) { return GetHeader(s); } - String GetRedirectUrl(); - int GetContentLength(); - int GetStatusCode() const { return status_code; } - String GetReasonPhrase() const { return reason_phrase; } - - String GetContent() const { return body; } - String operator~() const { return GetContent(); } - operator String() const { return GetContent(); } - void ClearContent() { body.Clear(); } - - enum Phase { - START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED - }; - - bool Do(); - int GetPhase() const { return phase; } - String GetPhaseName() const; - bool InProgress() const { return phase != FAILED && phase != FINISHED; } - bool IsFailure() const { return phase == FAILED; } - bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; } - - String Execute(); - - HttpRequest(); - HttpRequest(const char *url); - - static void Trace(bool b = true); -}; +String FormatIP(dword _ip); + +String UrlEncode(const String& s); +String UrlEncode(const String& s, const char *specials); +String UrlDecode(const char *b, const char *e); +inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); } + +String Base64Encode(const char *b, const char *e); +inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); } +String Base64Decode(const char *b, const char *e); +inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); } + +class IpAddrInfo { + enum { COUNT = 32 }; + struct Entry { + const char *host; + const char *port; + int status; + addrinfo *addr; + }; + static Entry pool[COUNT]; + + enum { + EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED + }; + + String host, port; + Entry *entry; + Entry exe[1]; + + static void EnterPool(); + static void LeavePool(); + static rawthread_t rawthread__ Thread(void *ptr); + + void Start(); + +public: + void Start(const String& host, int port); + bool InProgress(); + bool Execute(const String& host, int port); + addrinfo *GetResult(); + void Clear(); + + IpAddrInfo(); + ~IpAddrInfo() { Clear(); } +}; + +enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 }; + +class TcpSocket { + enum { BUFFERSIZE = 512 }; + SOCKET socket; + char buffer[BUFFERSIZE]; + char *ptr; + char *end; + bool is_eof; + bool is_error; + bool is_abort; + bool ipv6; + + int timeout; + int waitstep; + int done; + + int errorcode; + String errordesc; + + struct SSLBase { + virtual void Secure(TcpSocket& s) = 0; + virtual void Send(TcpSocket& s) = 0; + virtual void Recv(TcpSocket& s) = 0; + }; + + One ssl; + + SOCKET AcceptRaw(dword *ipaddr, int timeout_msec); + bool Open(int family, int type, int protocol); + int Recv(void *buffer, int maxlen); + int Send(const void *buffer, int maxlen); + bool RawConnect(addrinfo *info); + void CreateSSL(); + + void ReadBuffer(); + int Get_(); + int Peek_(); + + void Reset(); + + void SetSockError(const char *context, const char *errdesc); + void SetSockError(const char *context); + + static int GetErrorCode(); + static bool WouldBlock(); + +public: + Callback WhenWait; + + static String GetHostName(); + + int GetDone() const { return done; } + + static void Init(); + + bool IsOpen() const { return socket != INVALID_SOCKET; } + bool IsEof() const { return is_eof && ptr == end; } + + bool IsError() const { return is_error; } + void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); } + int GetError() const { return errorcode; } + String GetErrorDesc() const { return errordesc; } + + void Abort() { is_abort = true; } + bool IsAbort() const { return is_abort; } + void ClearAbort() { is_abort = false; } + + SOCKET GetSOCKET() const { return socket; } + String GetPeerAddr() const; + + void Attach(SOCKET socket); + bool Connect(const char *host, int port); + bool Connect(IpAddrInfo& info); + bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true); + bool Accept(TcpSocket& listen_socket); + void Close(); + void Shutdown(); + + void NoDelay(); + void Linger(int msecs); + void NoLinger() { Linger(Null); } + void Reuse(bool reuse = true); + + bool Wait(dword events); + bool WaitRead() { return Wait(WAIT_READ); } + bool WaitWrite() { return Wait(WAIT_WRITE); } + + int Peek() { return ptr < end ? *ptr : Peek_(); } + int Term() { return Peek(); } + int Get() { return ptr < end ? *ptr++ : Get_(); } + int Get(void *buffer, int len); + String Get(int len); + int GetAll(void *buffer, int len) { return Get(buffer, len) == len; } + String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); } + String GetLine(int maxlen = 2000000); + + int Put(const char *s, int len); + int Put(const String& s) { return Put(s.Begin(), s.GetLength()); } + bool PutAll(const char *s, int len) { return Put(s, len) == len; } + bool PutAll(const String& s) { return Put(s) == s.GetCount(); } + + TcpSocket& Timeout(int ms) { timeout = ms; return *this; } + int GetTimeout() const { return timeout; } + TcpSocket& Blocking() { return Timeout(Null); } + + TcpSocket(); + ~TcpSocket() { Close(); } +}; + +class SocketWaitEvent { + Vector< Tuple2 > socket; + fd_set read[1], write[1], exception[1]; + +public: + void Clear() { socket.Clear(); } + void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); } + void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); } + int Wait(int timeout); + dword Get(int i) const; + dword operator[](int i) const { return Get(i); } + + SocketWaitEvent(); +}; + +struct HttpHeader { + String first_line; + VectorMap fields; + + String operator[](const char *id) { return fields.Get(id, Null); } + + bool Response(String& protocol, int& code, String& reason); + bool Request(String& method, String& uri, String& version); + + void Clear(); + bool Parse(const String& hdrs); +}; + +class HttpRequest : public TcpSocket { + int phase; + String data; + int count; + + HttpHeader header; + + String error; + String body; + + enum { + DEFAULT_HTTP_PORT = 80, + }; + + enum { + METHOD_GET, + METHOD_POST, + METHOD_HEAD, + METHOD_PUT, + }; + + int max_header_size; + int max_content_size; + int max_redirects; + int max_retries; + int timeout; + + String host; + int port; + String proxy_host; + int proxy_port; + String proxy_username; + String proxy_password; + String path; + + int method; + String accept; + String agent; + bool force_digest; + bool is_post; + bool std_headers; + bool hasurlvar; + String contenttype; + String username; + String password; + String digest; + String request_headers; + String postdata; + + String protocol; + int status_code; + String reason_phrase; + + int start_time; + int retry_count; + int redirect_count; + + int chunk; + + IpAddrInfo addrinfo; + int bodylen; + bool gzip; + Zlib z; + + void Init(); + + void StartPhase(int s); + void Start(); + void Dns(); + void StartRequest(); + bool SendingData(); + bool ReadingHeader(); + void StartBody(); + bool ReadingBody(); + void ReadingChunkHeader(); + void Finish(); + + void HttpError(const char *s); + void ContentOut(const void *ptr, dword size); + void Out(const void *ptr, dword size); + + String CalculateDigest(const String& authenticate) const; + +public: + Callback2 WhenContent; + + HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; } + HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; } + HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; } + HttpRequest& MaxRetries(int n) { max_retries = n; return *this; } + HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; } + HttpRequest& ChunkSize(int n) { chunk = n; return *this; } + + HttpRequest& Method(int m) { method = m; return *this; } + HttpRequest& GET() { return Method(METHOD_GET); } + HttpRequest& POST() { return Method(METHOD_POST); } + HttpRequest& HEAD() { return Method(METHOD_HEAD); } + HttpRequest& PUT() { return Method(METHOD_PUT); } + + HttpRequest& Host(const String& h) { host = h; return *this; } + HttpRequest& Port(int p) { port = p; return *this; } + HttpRequest& Path(const String& p) { path = p; return *this; } + HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; } + HttpRequest& Digest() { force_digest = true; return *this; } + HttpRequest& Digest(const String& d) { digest = d; return *this; } + HttpRequest& Url(const char *url); + HttpRequest& UrlVar(const char *id, const String& data); + HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); } + HttpRequest& PostData(const String& pd) { postdata = pd; return *this; } + HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); } + HttpRequest& Post(const String& data) { POST(); return PostData(data); } + HttpRequest& Post(const char *id, const String& data); + + HttpRequest& Headers(const String& h) { request_headers = h; return *this; } + HttpRequest& ClearHeaders() { return Headers(Null); } + HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; } + HttpRequest& Header(const char *id, const String& data); + + HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; } + HttpRequest& NoStdHeaders() { return StdHeaders(false); } + HttpRequest& Accept(const String& a) { accept = a; return *this; } + HttpRequest& Agent(const String& a) { agent = a; return *this; } + HttpRequest& ContentType(const String& a) { contenttype = a; return *this; } + + HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; } + HttpRequest& Proxy(const char *url); + HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; } + + bool IsSocketError() const { return TcpSocket::IsError(); } + bool IsHttpError() const { return !IsNull(error) ; } + bool IsError() const { return IsSocketError() || IsHttpError(); } + String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; } + void ClearError() { TcpSocket::ClearError(); error.Clear(); } + + String GetHeader(const char *s) { return header[s]; } + String operator[](const char *s) { return GetHeader(s); } + String GetRedirectUrl(); + int GetContentLength(); + int GetStatusCode() const { return status_code; } + String GetReasonPhrase() const { return reason_phrase; } + + String GetContent() const { return body; } + String operator~() const { return GetContent(); } + operator String() const { return GetContent(); } + void ClearContent() { body.Clear(); } + + enum Phase { + START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED + }; + + bool Do(); + int GetPhase() const { return phase; } + String GetPhaseName() const; + bool InProgress() const { return phase != FAILED && phase != FINISHED; } + bool IsFailure() const { return phase == FAILED; } + bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; } + + String Execute(); + + HttpRequest(); + HttpRequest(const char *url); + + static void Trace(bool b = true); +}; diff --git a/uppsrc/Core/src.tpp/Stream$en-us.tpp b/uppsrc/Core/src.tpp/Stream$en-us.tpp index 7db194a65..b1943fb99 100644 --- a/uppsrc/Core/src.tpp/Stream$en-us.tpp +++ b/uppsrc/Core/src.tpp/Stream$en-us.tpp @@ -324,15 +324,15 @@ by GetLastError call in Win32 or in errno in Posix). This error can be interpreted by GetErrorMessage function.&] [s3; &] [s4;%- &] -[s5;K%- [@(0.0.255) int]_[* GetError]()_[@(0.0.255) const]&] -[s7; [*/ Return value]-|Current error`-code. Zero indicates no error.&] +[s5;:Stream`:`:GetError`(`)const:%- [@(0.0.255) int]_[* GetError]()_[@(0.0.255) const]&] +[s2; Returns current error`-code. Zero indicates no error.&] [s3; &] [s4;%- &] [s5;:Stream`:`:ClearError`(`):%- [@(0.0.255) void]_[* ClearError]()&] [s2; Clears error code.&] [s3; &] [s4;%- &] -[s5;K%- [_^int64^ int64]_[* GetPos]()_[@(0.0.255) const]&] +[s5;:Stream`:`:GetPos`(`)const:%- [_^int64^ int64]_[* GetPos]()_[@(0.0.255) const]&] [s7; [*/ Return value]-|Current position in the stream.&] [s3; &] [s4;%- &] @@ -341,9 +341,10 @@ can be interpreted by GetErrorMessage function.&] Is also true in case of error.&] [s3; &] [s4;%- &] -[s5;K%- [_^int64^ int64]_[* GetLeft]()_[@(0.0.255) const]&] +[s5;:Stream`:`:GetLeft`(`)const:%- [_^int64^ int64]_[* GetLeft]()_[@(0.0.255) const]&] [s7; [*/ Return value]-|Bytes between current position and the end of stream `- equivalent to GetSize() `- GetPos().&] +[s3; &] [s4;%- &] [s5;:Stream`:`:SeekEnd`(int64`):%- [@(0.0.255) void]_[* SeekEnd]([_^int64^ int64]_[*@3 rel]_`= _[@3 0])&] @@ -364,11 +365,11 @@ position. Same as Seek(GetPos() `+ rel).&] [s3; &] [s4;%- &] [s5;:Stream`:`:Term`(`):%- [@(0.0.255) int]_[* Term]()&] +[s5;:Stream`:`:Peek`(`):%- [@(0.0.255) int]_[* Peek]()&] [s2; Peeks byte from input stream not advancing current position. If there are no more bytes in input stream or error occurred, negative value is returned.&] -[s7; [*/ Return value]-|Byte at current position in the stream.&] -[s3; &] +[s3;%- &] [s4;%- &] [s5;:Stream`:`:Get`(`):%- [@(0.0.255) int]_[* Get]()&] [s2; Reads single byte from input stream, advancing current position. @@ -377,8 +378,8 @@ negative value is returned.&] [s7; [*/ Return value]-|Byte read from input stream.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:Peek`(int`):%- [@(0.0.255) const]_[_^byte^ byte]_`*[* Peek]([@(0.0.255) int]_[*@3 s -ize]_`=_[@3 1])&] +[s5;:Stream`:`:PeekPtr`(int`):%- [@(0.0.255) const]_[_^byte^ byte]_`*[* PeekPtr]([@(0.0.255) i +nt]_[*@3 size]_`=_[@3 1])&] [s2; This is a special optimization method; it might return a pointer to data of [%-*@3 size] bytes at current position in the stream, but it is allowed to return NULL `- in that case you need to @@ -393,25 +394,20 @@ but it is allowed to return NULL `- in that case you need to output data using Put. Advances stream by [%-*@3 size].&] [s3; &] [s4;%- &] -[s5;:Stream`:`:Put`(const void`*`,dword`):%- [@(0.0.255) void]_[* Put]([@(0.0.255) const]_[@(0.0.255) v -oid]_`*[*@3 data], [_^dword^ dword]_[*@3 size])&] +[s5;:Stream`:`:Put`(const void`*`,int`):%- [@(0.0.255) void]_[* Put]([@(0.0.255) const]_[@(0.0.255) v +oid]_`*[*@3 data], [@(0.0.255) int]_[*@3 size])&] [s2; Writes a block of raw binary data to the output stream.&] -[s7; [%-*C@3 data]-|Pointer to data.&] -[s7; [%-*C@3 size]-|Number of bytes to write.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:Get`(void`*`,dword`):%- [_^dword^ dword]_[* Get]([@(0.0.255) void]_`*[*@3 data -], [_^dword^ dword]_[*@3 size])&] -[s2; Reads a block of raw binary data from the input stream.&] -[s7; [%-*C@3 data]-|Pointer to buffer to receive the data.&] -[s7; [%-*C@3 size]-|Number of bytes to read.&] -[s7; [*/ Return value]-|Number of bytes actually read (lower or equal -to the requested [*@3 size]).&] +[s5;:Stream`:`:Get`(void`*`,int`):%- [@(0.0.255) int]_[* Get]([@(0.0.255) void]_`*[*@3 data], + [@(0.0.255) int]_[*@3 size])&] +[s2; Reads at most [%-*@3 size] bytes from the stream to [%-*@3 data]. +Returns the number of bytes actually read.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:Get`(dword`):%- [_^String^ String]_[* Get]([_^dword^ dword]_[*@3 size])&] -[s2; Reads a block of raw binary data from the input stream. The -number of bytes read is the length of String.&] +[s5;:Stream`:`:Get`(int`):%- [_^String^ String]_[* Get]([@(0.0.255) int]_[*@3 size])&] +[s2; Reads at most [%-*@3 size] bytes from the input stream and returns +result as String.&] [s3; &] [s4;%- &] [s5;:Stream`:`:LoadThrowing`(`):%- [@(0.0.255) void]_[* LoadThrowing]()&] @@ -677,8 +673,8 @@ the end is written.&] [s7; [*/ Return value]-|true if stream is in storing mode.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:SerializeRaw`(byte`*`,dword`):%- [@(0.0.255) void]_[* SerializeRaw]([_^byte^ b -yte]_`*[*@3 data], [_^dword^ dword]_[*@3 count])&] +[s5;:Stream`:`:SerializeRaw`(byte`*`,int`):%- [@(0.0.255) void]_[* SerializeRaw]([_^byte^ b +yte]_`*[*@3 data], [@(0.0.255) int]_[*@3 count])&] [s2; Serializes raw 8`-bit data. Might invoke LoadError if there is not enough data to load.&] [s7; [%-*C@3 data]-|Pointer to data to store or buffer to receive loaded @@ -686,8 +682,8 @@ data.&] [s7; [%-*C@3 count]-|Number of bytes to load/store.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:SerializeRaw`(word`*`,dword`):%- [@(0.0.255) void]_[* SerializeRaw]([_^word^ w -ord]_`*[*@3 data], [_^dword^ dword]_[*@3 count])&] +[s5;:Stream`:`:SerializeRaw`(word`*`,int`):%- [@(0.0.255) void]_[* SerializeRaw]([_^word^ w +ord]_`*[*@3 data], [@(0.0.255) int]_[*@3 count])&] [s2; Serializes raw 16`-bit data. Might invoke LoadError if there is not enough data to load.&] [s7; [%-*C@3 data]-|Pointer to data to store or buffer to receive loaded @@ -695,8 +691,8 @@ data.&] [s7; [%-*C@3 count]-|Number of values to load/store.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:SerializeRaw`(dword`*`,dword`):%- [@(0.0.255) void]_[* SerializeRaw]([_^dword^ d -word]_`*[*@3 data], [_^dword^ dword]_[*@3 count])&] +[s5;:Stream`:`:SerializeRaw`(dword`*`,int`):%- [@(0.0.255) void]_[* SerializeRaw]([_^dword^ d +word]_`*[*@3 data], [@(0.0.255) int]_[*@3 count])&] [s2; Serializes raw 32`-bit data. Might invoke LoadError if there is not enough data to load.&] [s7; [%-*C@3 data]-|Pointer to data to store or buffer to receive loaded @@ -704,8 +700,8 @@ data.&] [s7; [%-*C@3 count]-|Number of values to load/store.&] [s3; &] [s4;%- &] -[s5;:Stream`:`:SerializeRaw`(uint64`*`,dword`):%- [@(0.0.255) void]_[* SerializeRaw]([_^uint64^ u -int64]_`*[*@3 data], [_^dword^ dword]_[*@3 count])&] +[s5;:Stream`:`:SerializeRaw`(uint64`*`,int`):%- [@(0.0.255) void]_[* SerializeRaw]([_^uint64^ u +int64]_`*[*@3 data], [@(0.0.255) int]_[*@3 count])&] [s2; Serializes raw 64`-bit data. Might invoke LoadError if there is not enough data to load.&] [s7; [%-*C@3 data]-|Pointer to data to store or buffer to receive loaded @@ -713,6 +709,11 @@ data.&] [s7; [%-*C@3 count]-|Number of values to load/store.&] [s3; &] [s4;%- &] +[s5;:Stream`:`:SerializeRLE`(byte`*`,int`):%- [@(0.0.255) void]_[* SerializeRLE]([_^byte^ b +yte]_`*[*@3 data], [@(0.0.255) int]_[*@3 count])&] +[s2; Serializes raw data, using simple RLE compression.&] +[s3; &] +[s4;%- &] [s5;:Stream`:`:operator`%`(bool`&`):%- [_^Stream^ Stream][@(0.0.255) `&]_[* operator%]([@(0.0.255) b ool`&]_[*@3 d])&] [s2; Serializes bool variable [%-*@3 d]. Might invoke LoadError if