From 9c22b7cc64a3bd074f7cec012bd2f29f210d4ccf Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 16 May 2016 01:05:40 +0600 Subject: CLOUD: Rewrite NetworkReadStream Now it is based on MemoryReadWriteStream, which is introduced by this commit. This stream is using ring buffer and is dynamically increasing its size when necessary. --- backends/cloud/curl/connectionmanager.cpp | 72 ---------------------- backends/cloud/curl/connectionmanager.h | 47 --------------- backends/cloud/curl/networkreadstream.cpp | 83 -------------------------- backends/cloud/curl/networkreadstream.h | 76 ----------------------- backends/cloud/dropbox/curlrequest.cpp | 4 +- backends/cloud/dropbox/curlrequest.h | 9 +-- backends/cloud/request.h | 4 +- backends/cloud/storage.h | 4 +- backends/module.mk | 10 +++- backends/networking/curl/connectionmanager.cpp | 71 ++++++++++++++++++++++ backends/networking/curl/connectionmanager.h | 47 +++++++++++++++ backends/networking/curl/networkreadstream.cpp | 70 ++++++++++++++++++++++ backends/networking/curl/networkreadstream.h | 75 +++++++++++++++++++++++ common/memstream.h | 83 ++++++++++++++++++++++++++ 14 files changed, 364 insertions(+), 291 deletions(-) delete mode 100644 backends/cloud/curl/connectionmanager.cpp delete mode 100644 backends/cloud/curl/connectionmanager.h delete mode 100644 backends/cloud/curl/networkreadstream.cpp delete mode 100644 backends/cloud/curl/networkreadstream.h create mode 100644 backends/networking/curl/connectionmanager.cpp create mode 100644 backends/networking/curl/connectionmanager.h create mode 100644 backends/networking/curl/networkreadstream.cpp create mode 100644 backends/networking/curl/networkreadstream.h diff --git a/backends/cloud/curl/connectionmanager.cpp b/backends/cloud/curl/connectionmanager.cpp deleted file mode 100644 index d0a0ad8b5c..0000000000 --- a/backends/cloud/curl/connectionmanager.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -#include "backends/cloud/curl/connectionmanager.h" -#include "backends/cloud/curl/networkreadstream.h" -#include "common/debug.h" -#include - -namespace Cloud { - -ConnectionManager::ConnectionManager(): _multi(0) { - curl_global_init(CURL_GLOBAL_ALL); - _multi = curl_multi_init(); -} - -ConnectionManager::~ConnectionManager() { - curl_multi_cleanup(_multi); - curl_global_cleanup(); -} - -NetworkReadStream *ConnectionManager::makeRequest(const char *url) { - NetworkReadStream *stream = new NetworkReadStream(url); - curl_multi_add_handle(_multi, stream->getEasyHandle()); - return stream; -} - -void ConnectionManager::handle() { - int U; - curl_multi_perform(_multi, &U); - - int Q; - CURLMsg *curlMsg; - while ((curlMsg = curl_multi_info_read(_multi, &Q))) { - if (curlMsg->msg == CURLMSG_DONE) { - CURL *e = curlMsg->easy_handle; - - NetworkReadStream *stream; - curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream); - if (stream) stream->done(); - - debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result)); - curl_multi_remove_handle(_multi, e); - } - else { - debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg); - //TODO: notify stream on this case also - } - } -} - -} //end of namespace Cloud diff --git a/backends/cloud/curl/connectionmanager.h b/backends/cloud/curl/connectionmanager.h deleted file mode 100644 index 0ea27201f2..0000000000 --- a/backends/cloud/curl/connectionmanager.h +++ /dev/null @@ -1,47 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#ifndef BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H -#define BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H - -#include "common/str.h" - -typedef void CURLM; - -namespace Cloud { - -class NetworkReadStream; - -class ConnectionManager { - CURLM *_multi; - -public: - ConnectionManager(); - virtual ~ConnectionManager(); - - NetworkReadStream *makeRequest(const char *url); - void handle(); -}; - -} //end of namespace Cloud - -#endif diff --git a/backends/cloud/curl/networkreadstream.cpp b/backends/cloud/curl/networkreadstream.cpp deleted file mode 100644 index 8adac67569..0000000000 --- a/backends/cloud/curl/networkreadstream.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -#include "backends/cloud/curl/networkreadstream.h" -#include "common/debug.h" -#include - -namespace Cloud { - -static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) { - NetworkReadStream *stream = (NetworkReadStream *)p; - if (stream) return stream->dataCallback(d, n, l); - return 0; -} - -NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) { - _easy = curl_easy_init(); - curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); - curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us - curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete - curl_easy_setopt(_easy, CURLOPT_HEADER, 0L); - curl_easy_setopt(_easy, CURLOPT_URL, url); - curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L); -} - -NetworkReadStream::~NetworkReadStream() { - curl_easy_cleanup(_easy); -} - -bool NetworkReadStream::eos() const { - return _eos; -} - -uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) { - uint32 available = _bytes.size(); - - if (available == 0) { - if (_requestComplete) _eos = true; - return 0; - } - - char *data = (char *)dataPtr; - uint32 actuallyRead = (dataSize < available ? dataSize : available); - for (uint32 i = 0; i < actuallyRead; ++i) data[i] = _bytes[i]; - data[actuallyRead] = 0; - _bytes.erase(0, actuallyRead); - return actuallyRead; -} - -void NetworkReadStream::done() { - _requestComplete = true; -} - -size_t NetworkReadStream::dataCallback(char *d, size_t n, size_t l) { - //TODO: return CURL_WRITEFUNC_PAUSE if _bytes is too long - //TODO: remember https://curl.haxx.se/libcurl/c/curl_easy_pause.html (Memory Use / compressed data case) - //TODO: if using pause, don't forget to unpause it somehow from read() up there - _bytes += Common::String(d, n*l); - return n*l; -} - -} //end of namespace Cloud diff --git a/backends/cloud/curl/networkreadstream.h b/backends/cloud/curl/networkreadstream.h deleted file mode 100644 index e469784e28..0000000000 --- a/backends/cloud/curl/networkreadstream.h +++ /dev/null @@ -1,76 +0,0 @@ -/* ScummVM - Graphic Adventure Engine -* -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT -* file distributed with this source distribution. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version 2 -* of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -* -*/ - -#ifndef BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H -#define BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H - -#include "common/stream.h" -#include "common/str.h" - -typedef void CURL; - -namespace Cloud { - -class NetworkReadStream: public Common::ReadStream { - CURL *_easy; - bool _eos, _requestComplete; - Common::String _bytes; - -public: - NetworkReadStream(const char *url); - virtual ~NetworkReadStream(); - - CURL *getEasyHandle() const { return _easy; } - - /** - * Returns true if a read failed because the stream end has been reached. - * This flag is cleared by clearErr(). - * For a SeekableReadStream, it is also cleared by a successful seek. - * - * @note The semantics of any implementation of this method are - * supposed to match those of ISO C feof(). In particular, in a stream - * with N bytes, reading exactly N bytes from the start should *not* - * set eos; only reading *beyond* the available data should set it. - */ - virtual bool eos() const; - - /** - * Read data from the stream. Subclasses must implement this - * method; all other read methods are implemented using it. - * - * @note The semantics of any implementation of this method are - * supposed to match those of ISO C fread(), in particular where - * it concerns setting error and end of file/stream flags. - * - * @param dataPtr pointer to a buffer into which the data is read - * @param dataSize number of bytes to be read - * @return the number of bytes which were actually read. - */ - virtual uint32 read(void *dataPtr, uint32 dataSize); - - void done(); - size_t dataCallback(char *d, size_t n, size_t l); -}; - -} //end of namespace Cloud - -#endif diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp index 1dfdd9e25f..ecc868cb51 100644 --- a/backends/cloud/dropbox/curlrequest.cpp +++ b/backends/cloud/dropbox/curlrequest.cpp @@ -23,7 +23,7 @@ #define FORBIDDEN_SYMBOL_ALLOW_ALL #include "backends/cloud/dropbox/curlrequest.h" -#include "backends/cloud/curl/networkreadstream.h" +#include "backends/networking/curl/networkreadstream.h" #include "common/debug.h" #include @@ -38,7 +38,7 @@ CurlRequest::~CurlRequest() { if (_stream) delete _stream; } -bool CurlRequest::handle(ConnectionManager& manager) { +bool CurlRequest::handle(Networking::ConnectionManager &manager) { if (_firstTime) { _stream = manager.makeRequest(_url); _firstTime = false; diff --git a/backends/cloud/dropbox/curlrequest.h b/backends/cloud/dropbox/curlrequest.h index 2c8ab6b8f8..3d5d4adb72 100644 --- a/backends/cloud/dropbox/curlrequest.h +++ b/backends/cloud/dropbox/curlrequest.h @@ -25,22 +25,23 @@ #include "backends/cloud/request.h" -namespace Cloud { - +namespace Networking { class NetworkReadStream; +} +namespace Cloud { namespace Dropbox { class CurlRequest : public Cloud::Request { bool _firstTime; const char *_url; - NetworkReadStream *_stream; + Networking::NetworkReadStream *_stream; public: CurlRequest(Callback cb, const char *url); virtual ~CurlRequest(); - virtual bool handle(ConnectionManager& manager); + virtual bool handle(Networking::ConnectionManager &manager); }; } //end of namespace Dropbox diff --git a/backends/cloud/request.h b/backends/cloud/request.h index b4f5ccab0b..d85a68d570 100644 --- a/backends/cloud/request.h +++ b/backends/cloud/request.h @@ -23,7 +23,7 @@ #ifndef BACKENDS_CLOUD_REQUEST_H #define BACKENDS_CLOUD_REQUEST_H -#include "backends/cloud/curl/connectionmanager.h" +#include "backends/networking/curl/connectionmanager.h" namespace Cloud { @@ -47,7 +47,7 @@ public: * @return true if request's work is complete and it may be removed from Storage's list */ - virtual bool handle(ConnectionManager& manager) = 0; + virtual bool handle(Networking::ConnectionManager &manager) = 0; }; } //end of namespace Cloud diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 0949ff8505..84b6157a22 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -26,7 +26,7 @@ #include "common/str.h" #include "common/array.h" #include "backends/cloud/request.h" -#include "backends/cloud/curl/connectionmanager.h" +#include "backends/networking/curl/connectionmanager.h" namespace Cloud { @@ -36,7 +36,7 @@ class Storage { protected: Common::Array _requests; - ConnectionManager _connectionManager; + Networking::ConnectionManager _connectionManager; virtual void addRequest(Request *request); //starts the timer if it's not started virtual void handler(); diff --git a/backends/module.mk b/backends/module.mk index cf100892f5..0be8ef90a5 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -24,9 +24,13 @@ MODULE_OBJS += \ cloud/manager.o \ cloud/storage.o \ cloud/dropbox/dropboxstorage.o \ - cloud/dropbox/curlrequest.o \ - cloud/curl/connectionmanager.o \ - cloud/curl/networkreadstream.o + cloud/dropbox/curlrequest.o +endif + +ifdef USE_LIBCURL +MODULE_OBJS += \ + networking/curl/connectionmanager.o \ + networking/curl/networkreadstream.o endif ifdef USE_ELF_LOADER diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp new file mode 100644 index 0000000000..b7b7112c7f --- /dev/null +++ b/backends/networking/curl/connectionmanager.cpp @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "backends/networking/curl/connectionmanager.h" +#include "backends/networking/curl/networkreadstream.h" +#include "common/debug.h" +#include + +namespace Networking { + +ConnectionManager::ConnectionManager(): _multi(0) { + curl_global_init(CURL_GLOBAL_ALL); + _multi = curl_multi_init(); +} + +ConnectionManager::~ConnectionManager() { + curl_multi_cleanup(_multi); + curl_global_cleanup(); +} + +NetworkReadStream *ConnectionManager::makeRequest(const char *url) { + NetworkReadStream *stream = new NetworkReadStream(url); + curl_multi_add_handle(_multi, stream->getEasyHandle()); + return stream; +} + +void ConnectionManager::handle() { + int U; + curl_multi_perform(_multi, &U); + + int Q; + CURLMsg *curlMsg; + while ((curlMsg = curl_multi_info_read(_multi, &Q))) { + if (curlMsg->msg == CURLMSG_DONE) { + CURL *e = curlMsg->easy_handle; + + NetworkReadStream *stream; + curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream); + if (stream) stream->done(); + + debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result)); + curl_multi_remove_handle(_multi, e); + } else { + debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg); + //TODO: notify stream on this case also + } + } +} + +} //end of namespace Cloud diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h new file mode 100644 index 0000000000..fadcdf1372 --- /dev/null +++ b/backends/networking/curl/connectionmanager.h @@ -0,0 +1,47 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H +#define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H + +#include "common/str.h" + +typedef void CURLM; + +namespace Networking { + +class NetworkReadStream; + +class ConnectionManager { + CURLM *_multi; + +public: + ConnectionManager(); + virtual ~ConnectionManager(); + + NetworkReadStream *makeRequest(const char *url); + void handle(); +}; + +} //end of namespace Cloud + +#endif diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp new file mode 100644 index 0000000000..5049ae507b --- /dev/null +++ b/backends/networking/curl/networkreadstream.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "backends/networking/curl/networkreadstream.h" +#include "common/debug.h" +#include + +namespace Networking { + +static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) { + NetworkReadStream *stream = (NetworkReadStream *)p; + if (stream) return stream->write(d, n*l); + return 0; +} + +NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) { + _easy = curl_easy_init(); + curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); + curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us + curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete + curl_easy_setopt(_easy, CURLOPT_HEADER, 0L); + curl_easy_setopt(_easy, CURLOPT_URL, url); + curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L); +} + +NetworkReadStream::~NetworkReadStream() { + curl_easy_cleanup(_easy); +} + +bool NetworkReadStream::eos() const { + return _eos; +} + +uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) { + uint32 actuallyRead = MemoryReadWriteStream::read(dataPtr, dataSize); + + if (actuallyRead == 0) { + if (_requestComplete) _eos = true; + return 0; + } + + return actuallyRead; +} + +void NetworkReadStream::done() { + _requestComplete = true; +} + +} //end of namespace Cloud diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h new file mode 100644 index 0000000000..9c7d8f83b6 --- /dev/null +++ b/backends/networking/curl/networkreadstream.h @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H +#define BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H + +#include "common/memstream.h" +#include "common/stream.h" +#include "common/str.h" + +typedef void CURL; + +namespace Networking { + +class NetworkReadStream: public Common::MemoryReadWriteStream { + CURL *_easy; + bool _eos, _requestComplete; + +public: + NetworkReadStream(const char *url); + virtual ~NetworkReadStream(); + + CURL *getEasyHandle() const { return _easy; } + + /** + * Returns true if a read failed because the stream end has been reached. + * This flag is cleared by clearErr(). + * For a SeekableReadStream, it is also cleared by a successful seek. + * + * @note The semantics of any implementation of this method are + * supposed to match those of ISO C feof(). In particular, in a stream + * with N bytes, reading exactly N bytes from the start should *not* + * set eos; only reading *beyond* the available data should set it. + */ + virtual bool eos() const; + + /** + * Read data from the stream. Subclasses must implement this + * method; all other read methods are implemented using it. + * + * @note The semantics of any implementation of this method are + * supposed to match those of ISO C fread(), in particular where + * it concerns setting error and end of file/stream flags. + * + * @param dataPtr pointer to a buffer into which the data is read + * @param dataSize number of bytes to be read + * @return the number of bytes which were actually read. + */ + virtual uint32 read(void *dataPtr, uint32 dataSize); + + void done(); +}; + +} //end of namespace Cloud + +#endif diff --git a/common/memstream.h b/common/memstream.h index 59d5f15b1a..963637cb27 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -209,6 +209,89 @@ public: bool seek(int32 offset, int whence = SEEK_SET); }; +/** +* MemoryStream based on RingBuffer. Grows if has insufficient buffer size. +*/ +class MemoryReadWriteStream : public WriteStream { +private: + uint32 _capacity; + uint32 _size; + byte *_data; + uint32 _writePos, _readPos, _pos, _length; + DisposeAfterUse::Flag _disposeMemory; + + void ensureCapacity(uint32 new_len) { + if (new_len <= _capacity) + return; + + byte *old_data = _data; + uint32 oldCapacity = _capacity; + + _capacity = MAX(new_len + 32, _capacity * 2); + _data = (byte *)malloc(_capacity); + + if (old_data) { + // Copy old data + if (_readPos < _writePos) { + memcpy(_data, old_data + _readPos, _writePos - _readPos); + _writePos -= _readPos; + _readPos = 0; + } else { + memcpy(_data, old_data + _readPos, oldCapacity - _readPos); + memcpy(_data + oldCapacity - _readPos, old_data, _writePos); + _writePos += oldCapacity - _readPos; + _readPos = 0; + } + free(old_data); + } + } +public: + MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory) {} + + ~MemoryReadWriteStream() { + if (_disposeMemory) + free(_data); + } + + uint32 write(const void *dataPtr, uint32 dataSize) { + ensureCapacity(_length + dataSize); + if (_writePos + dataSize < _capacity) { + memcpy(_data + _writePos, dataPtr, dataSize); + } else { + memcpy(_data + _writePos, dataPtr, _capacity - _writePos); + const byte *shiftedPtr = (const byte *)dataPtr + _capacity - _writePos; + memcpy(_data, shiftedPtr, dataSize - (_capacity - _writePos)); + } + _writePos = (_writePos + dataSize) % _capacity; + _pos += dataSize; + _length += dataSize; + if (_pos > _size) + _size = _pos; + return dataSize; + } + + uint32 read(void *dataPtr, uint32 dataSize) { + uint32 length = _length; + if (length < dataSize) dataSize = length; + if (dataSize == 0 || _capacity == 0) return 0; + if (_readPos + dataSize < _capacity) { + memcpy(dataPtr, _data + _readPos, dataSize); + } else { + memcpy(dataPtr, _data + _readPos, _capacity - _readPos); + byte *shiftedPtr = (byte *)dataPtr + _capacity - _readPos; + memcpy(shiftedPtr, _data, dataSize - (_capacity - _readPos)); + } + _readPos = (_readPos + dataSize) % _capacity; + _length -= dataSize; + return dataSize; + } + + uint32 pos() const { return _pos - _length; } //'read' position in the stream + uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only + + byte *getData() { return _data; } +}; + } // End of namespace Common #endif -- cgit v1.2.3