ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

stream.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/ptr.h"
00024 #include "common/stream.h"
00025 #include "common/memstream.h"
00026 #include "common/substream.h"
00027 #include "common/str.h"
00028 
00029 namespace Common {
00030 
00031 uint32 WriteStream::writeStream(ReadStream *stream, uint32 dataSize) {
00032     void *buf = malloc(dataSize);
00033     dataSize = stream->read(buf, dataSize);
00034     assert(dataSize > 0);
00035     dataSize = write(buf, dataSize);
00036     free(buf);
00037     return dataSize;
00038 }
00039 
00040 uint32 WriteStream::writeStream(SeekableReadStream *stream) {
00041     return writeStream(stream, stream->size());
00042 }
00043 
00044 void WriteStream::writeString(const String &str) {
00045     write(str.c_str(), str.size());
00046 }
00047 
00048 SeekableReadStream *ReadStream::readStream(uint32 dataSize) {
00049     void *buf = malloc(dataSize);
00050     dataSize = read(buf, dataSize);
00051     assert(dataSize > 0);
00052     return new MemoryReadStream((byte *)buf, dataSize, DisposeAfterUse::YES);
00053 }
00054 
00055 Common::String ReadStream::readPascalString(bool transformCR) {
00056     Common::String s;
00057     char *buf;
00058     int len;
00059     int i;
00060 
00061     len = readByte();
00062     buf = (char *)malloc(len + 1);
00063     for (i = 0; i < len; i++) {
00064         buf[i] = readByte();
00065         if (transformCR && buf[i] == 0x0d)
00066             buf[i] = '\n';
00067     }
00068 
00069     buf[i] = 0;
00070 
00071     s = buf;
00072     free(buf);
00073 
00074     return s;
00075 }
00076 
00077 uint32 MemoryReadStream::read(void *dataPtr, uint32 dataSize) {
00078     // Read at most as many bytes as are still available...
00079     if (dataSize > _size - _pos) {
00080         dataSize = _size - _pos;
00081         _eos = true;
00082     }
00083     memcpy(dataPtr, _ptr, dataSize);
00084 
00085     _ptr += dataSize;
00086     _pos += dataSize;
00087 
00088     return dataSize;
00089 }
00090 
00091 bool MemoryReadStream::seek(int32 offs, int whence) {
00092     // Pre-Condition
00093     assert(_pos <= _size);
00094     switch (whence) {
00095     case SEEK_END:
00096         // SEEK_END works just like SEEK_SET, only 'reversed',
00097         // i.e. from the end.
00098         offs = _size + offs;
00099         // Fall through
00100     case SEEK_SET:
00101         // Fall through
00102     default:
00103         _ptr = _ptrOrig + offs;
00104         _pos = offs;
00105         break;
00106 
00107     case SEEK_CUR:
00108         _ptr += offs;
00109         _pos += offs;
00110         break;
00111     }
00112     // Post-Condition
00113     assert(_pos <= _size);
00114 
00115     // Reset end-of-stream flag on a successful seek
00116     _eos = false;
00117     return true; // FIXME: STREAM REWRITE
00118 }
00119 
00120 #pragma mark -
00121 
00122 enum {
00123     LF = 0x0A,
00124     CR = 0x0D
00125 };
00126 
00127 char *SeekableReadStream::readLine(char *buf, size_t bufSize) {
00128     assert(buf != nullptr && bufSize > 1);
00129     char *p = buf;
00130     size_t len = 0;
00131     char c = 0;
00132 
00133     // If end-of-file occurs before any characters are read, return NULL
00134     // and the buffer contents remain unchanged.
00135     if (eos() || err()) {
00136         return nullptr;
00137     }
00138 
00139     // Loop as long as there is still free space in the buffer,
00140     // and the line has not ended
00141     while (len + 1 < bufSize && c != LF) {
00142         c = readByte();
00143 
00144         if (eos()) {
00145             // If end-of-file occurs before any characters are read, return
00146             // NULL and the buffer contents remain unchanged.
00147             if (len == 0)
00148                 return nullptr;
00149 
00150             break;
00151         }
00152 
00153         // If an error occurs, return NULL and the buffer contents
00154         // are indeterminate.
00155         if (err())
00156             return nullptr;
00157 
00158         // Check for CR or CR/LF
00159         // * DOS and Windows use CRLF line breaks
00160         // * Unix and OS X use LF line breaks
00161         // * Macintosh before OS X used CR line breaks
00162         if (c == CR) {
00163             // Look at the next char -- is it LF? If not, seek back
00164             c = readByte();
00165 
00166             if (err()) {
00167                 return nullptr; // error: the buffer contents are indeterminate
00168             }
00169             if (eos()) {
00170                 // The CR was the last character in the file.
00171                 // Reset the eos() flag since we successfully finished a line
00172                 clearErr();
00173             } else if (c != LF) {
00174                 seek(-1, SEEK_CUR);
00175             }
00176 
00177             // Treat CR & CR/LF as plain LF
00178             c = LF;
00179         }
00180 
00181         *p++ = c;
00182         len++;
00183     }
00184 
00185     // We always terminate the buffer if no error occurred
00186     *p = 0;
00187     return buf;
00188 }
00189 
00190 String SeekableReadStream::readLine() {
00191     // Read a line
00192     String line;
00193     while (line.lastChar() != '\n') {
00194         char buf[256];
00195         if (!readLine(buf, 256))
00196             break;
00197         line += buf;
00198     }
00199 
00200     if (line.lastChar() == '\n')
00201         line.deleteLastChar();
00202 
00203     return line;
00204 }
00205 
00206 uint32 SubReadStream::read(void *dataPtr, uint32 dataSize) {
00207     if (dataSize > _end - _pos) {
00208         dataSize = _end - _pos;
00209         _eos = true;
00210     }
00211 
00212     dataSize = _parentStream->read(dataPtr, dataSize);
00213     _pos += dataSize;
00214 
00215     return dataSize;
00216 }
00217 
00218 SeekableSubReadStream::SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream)
00219     : SubReadStream(parentStream, end, disposeParentStream),
00220     _parentStream(parentStream),
00221     _begin(begin) {
00222     assert(_begin <= _end);
00223     _pos = _begin;
00224     _parentStream->seek(_pos);
00225     _eos = false;
00226 }
00227 
00228 bool SeekableSubReadStream::seek(int32 offset, int whence) {
00229     assert(_pos >= _begin);
00230     assert(_pos <= _end);
00231 
00232     switch (whence) {
00233     case SEEK_END:
00234         offset = size() + offset;
00235         // fallthrough
00236     case SEEK_SET:
00237         // Fall through
00238     default:
00239         _pos = _begin + offset;
00240         break;
00241     case SEEK_CUR:
00242         _pos += offset;
00243     }
00244 
00245     assert(_pos >= _begin);
00246     assert(_pos <= _end);
00247 
00248     bool ret = _parentStream->seek(_pos);
00249     if (ret) _eos = false; // reset eos on successful seek
00250 
00251     return ret;
00252 }
00253 
00254 uint32 SafeSeekableSubReadStream::read(void *dataPtr, uint32 dataSize) {
00255     // Make sure the parent stream is at the right position
00256     seek(0, SEEK_CUR);
00257 
00258     return SeekableSubReadStream::read(dataPtr, dataSize);
00259 }
00260 
00261 void SeekableReadStream::hexdump(int len, int bytesPerLine, int startOffset) {
00262     uint pos_ = pos();
00263     uint size_ = size();
00264     uint toRead = MIN<uint>(len + startOffset, size_ - pos_);
00265     byte *data = (byte *)calloc(toRead, 1);
00266 
00267     read(data, toRead);
00268     Common::hexdump(data, toRead, bytesPerLine, startOffset);
00269 
00270     free(data);
00271 
00272     seek(pos_);
00273 }
00274 
00275 #pragma mark -
00276 
00277 namespace {
00278 
00284 class BufferedReadStream : virtual public ReadStream {
00285 protected:
00286     DisposablePtr<ReadStream> _parentStream;
00287     byte *_buf;
00288     uint32 _pos;
00289     bool _eos; // end of stream
00290     uint32 _bufSize;
00291     uint32 _realBufSize;
00292 
00293 public:
00294     BufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream);
00295     virtual ~BufferedReadStream();
00296 
00297     virtual bool eos() const { return _eos; }
00298     virtual bool err() const { return _parentStream->err(); }
00299     virtual void clearErr() { _eos = false; _parentStream->clearErr(); }
00300 
00301     virtual uint32 read(void *dataPtr, uint32 dataSize);
00302 };
00303 
00304 BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream)
00305     : _parentStream(parentStream, disposeParentStream),
00306     _pos(0),
00307     _eos(false),
00308     _bufSize(0),
00309     _realBufSize(bufSize) {
00310 
00311     assert(parentStream);
00312     _buf = new byte[bufSize];
00313     assert(_buf);
00314 }
00315 
00316 BufferedReadStream::~BufferedReadStream() {
00317     delete[] _buf;
00318 }
00319 
00320 uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) {
00321     uint32 alreadyRead = 0;
00322     const uint32 bufBytesLeft = _bufSize - _pos;
00323 
00324     // Check whether the data left in the buffer suffices....
00325     if (dataSize > bufBytesLeft) {
00326         // Nope, we need to read more data
00327 
00328         // First, flush the buffer, if it is non-empty
00329         if (0 < bufBytesLeft) {
00330             memcpy(dataPtr, _buf + _pos, bufBytesLeft);
00331             _pos = _bufSize;
00332             alreadyRead += bufBytesLeft;
00333             dataPtr = (byte *)dataPtr + bufBytesLeft;
00334             dataSize -= bufBytesLeft;
00335         }
00336 
00337         // At this point the buffer is empty. Now if the read request
00338         // exceeds the buffer size, just satisfy it directly.
00339         if (dataSize > _realBufSize) {
00340             uint32 n = _parentStream->read(dataPtr, dataSize);
00341             if (_parentStream->eos())
00342                 _eos = true;
00343 
00344             // Fill the buffer from the user buffer so a seek back in
00345             // the stream into the buffered area brings consistent data.
00346             _bufSize = MIN(n, _realBufSize);
00347             _pos = _bufSize;
00348             memcpy(_buf, (byte *)dataPtr + n - _bufSize, _bufSize);
00349 
00350             return alreadyRead + n;
00351         }
00352 
00353         // Refill the buffer.
00354         // If we didn't read as many bytes as requested, the reason
00355         // is EOF or an error. In that case we truncate the buffer
00356         // size, as well as the number of  bytes we are going to
00357         // return to the caller.
00358         _bufSize = _parentStream->read(_buf, _realBufSize);
00359         _pos = 0;
00360         if (_bufSize < dataSize) {
00361             // we didn't get enough data from parent
00362             if (_parentStream->eos())
00363                 _eos = true;
00364             dataSize = _bufSize;
00365         }
00366     }
00367 
00368     if (dataSize) {
00369         // Satisfy the request from the buffer
00370         memcpy(dataPtr, _buf + _pos, dataSize);
00371         _pos += dataSize;
00372     }
00373     return alreadyRead + dataSize;
00374 }
00375 
00376 } // End of anonymous namespace
00377 
00378 
00379 ReadStream *wrapBufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) {
00380     if (parentStream)
00381         return new BufferedReadStream(parentStream, bufSize, disposeParentStream);
00382     return nullptr;
00383 }
00384 
00385 #pragma mark -
00386 
00387 namespace {
00388 
00393 class BufferedSeekableReadStream : public BufferedReadStream, public SeekableReadStream {
00394 protected:
00395     SeekableReadStream *_parentStream;
00396 public:
00397     BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO);
00398 
00399     virtual int32 pos() const { return _parentStream->pos() - (_bufSize - _pos); }
00400     virtual int32 size() const { return _parentStream->size(); }
00401 
00402     virtual bool seek(int32 offset, int whence = SEEK_SET);
00403 };
00404 
00405 BufferedSeekableReadStream::BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream)
00406     : BufferedReadStream(parentStream, bufSize, disposeParentStream),
00407     _parentStream(parentStream) {
00408 }
00409 
00410 bool BufferedSeekableReadStream::seek(int32 offset, int whence) {
00411     // If it is a "local" seek, we may get away with "seeking" around
00412     // in the buffer only.
00413     _eos = false; // seeking always cancels EOS
00414 
00415     int relOffset = 0;
00416     switch (whence) {
00417     case SEEK_SET:
00418         relOffset = offset - pos();
00419         break;
00420     case SEEK_CUR:
00421         relOffset = offset;
00422         break;
00423     case SEEK_END:
00424         relOffset = (size() + offset) - pos();
00425         break;
00426     default:
00427         break;
00428     }
00429 
00430     if ((int)_pos + relOffset >= 0 && _pos + relOffset <= _bufSize) {
00431         _pos += relOffset;
00432 
00433         // Note: we do not need to reset parent's eos flag here. It is
00434         // sufficient that it is reset when actually seeking in the parent.
00435     } else {
00436         // Seek was not local enough, so we reset the buffer and
00437         // just seek normally in the parent stream.
00438         if (whence == SEEK_CUR)
00439             offset -= (_bufSize - _pos);
00440         // We invalidate the buffer here. This assures that successive seeks
00441         // do not have the chance to incorrectly think they seeked back into
00442         // the buffer.
00443         // Note: This does not take full advantage of the buffer. But it is
00444         // a simple way to prevent nasty errors. It would be possible to take
00445         // full advantage of the buffer by saving its actual start position.
00446         // This seems not worth the effort for this seemingly uncommon use.
00447         _pos = _bufSize = 0;
00448         _parentStream->seek(offset, whence);
00449     }
00450 
00451     return true;
00452 }
00453 
00454 } // End of anonymous namespace
00455 
00456 SeekableReadStream *wrapBufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) {
00457     if (parentStream)
00458         return new BufferedSeekableReadStream(parentStream, bufSize, disposeParentStream);
00459     return nullptr;
00460 }
00461 
00462 #pragma mark -
00463 
00464 namespace {
00465 
00469 class BufferedWriteStream : public WriteStream {
00470 protected:
00471     WriteStream *_parentStream;
00472     byte *_buf;
00473     uint32 _pos;
00474     const uint32 _bufSize;
00475 
00483     bool flushBuffer() {
00484         const uint32 bytesToWrite = _pos;
00485 
00486         if (bytesToWrite) {
00487             _pos = 0;
00488             if (_parentStream->write(_buf, bytesToWrite) != bytesToWrite)
00489                 return false;
00490         }
00491         return true;
00492     }
00493 
00494 public:
00495     BufferedWriteStream(WriteStream *parentStream, uint32 bufSize)
00496         : _parentStream(parentStream),
00497         _pos(0),
00498         _bufSize(bufSize) {
00499 
00500         assert(parentStream);
00501         _buf = new byte[bufSize];
00502         assert(_buf);
00503     }
00504 
00505     virtual ~BufferedWriteStream() {
00506         const bool flushResult = flushBuffer();
00507         assert(flushResult);
00508 
00509         delete _parentStream;
00510 
00511         delete[] _buf;
00512     }
00513 
00514     virtual uint32 write(const void *dataPtr, uint32 dataSize) {
00515         // check if we have enough space for writing to the buffer
00516         if (_bufSize - _pos >= dataSize) {
00517             memcpy(_buf + _pos, dataPtr, dataSize);
00518             _pos += dataSize;
00519         } else if (_bufSize >= dataSize) {  // check if we can flush the buffer and load the data
00520             const bool flushResult = flushBuffer();
00521             assert(flushResult);
00522             memcpy(_buf, dataPtr, dataSize);
00523             _pos += dataSize;
00524         } else  {   // too big for our buffer
00525             const bool flushResult = flushBuffer();
00526             assert(flushResult);
00527             return _parentStream->write(dataPtr, dataSize);
00528         }
00529         return dataSize;
00530     }
00531 
00532     virtual bool flush() { return flushBuffer(); }
00533 
00534     virtual int32 pos() const { return _pos; }
00535 
00536 };
00537 
00538 } // End of anonymous namespace
00539 
00540 WriteStream *wrapBufferedWriteStream(WriteStream *parentStream, uint32 bufSize) {
00541     if (parentStream)
00542         return new BufferedWriteStream(parentStream, bufSize);
00543     return nullptr;
00544 }
00545 
00546 } // End of namespace Common


Generated on Sat May 23 2020 05:00:31 for ResidualVM by doxygen 1.7.1
curved edge   curved edge