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


Generated on Sat Dec 14 2019 05:00:33 for ResidualVM by doxygen 1.7.1
curved edge   curved edge