diff --git a/doomsday/libdeng2/data.pri b/doomsday/libdeng2/data.pri index 5827323ab3..0a3ac69e5a 100644 --- a/doomsday/libdeng2/data.pri +++ b/doomsday/libdeng2/data.pri @@ -16,6 +16,7 @@ HEADERS += \ include/de/Guard \ include/de/IBlock \ include/de/IByteArray \ + include/de/IIOStream \ include/de/IReadable \ include/de/ISerializable \ include/de/IWritable \ @@ -54,6 +55,7 @@ HEADERS += \ include/de/data/huffman.h \ include/de/data/iblock.h \ include/de/data/ibytearray.h \ + include/de/data/iiostream.h \ include/de/data/info.h \ include/de/data/ireadable.h \ include/de/data/iserializable.h \ diff --git a/doomsday/libdeng2/filesys.pri b/doomsday/libdeng2/filesys.pri index eb204722b3..4daf645103 100644 --- a/doomsday/libdeng2/filesys.pri +++ b/doomsday/libdeng2/filesys.pri @@ -1,6 +1,7 @@ HEADERS += \ include/de/ArchiveFeed \ include/de/ArchiveFile \ + include/de/ByteArrayFile \ include/de/DirectoryFeed \ include/de/Feed \ include/de/File \ @@ -11,6 +12,7 @@ HEADERS += \ include/de/NativePath \ include/de/filesys/archivefeed.h \ include/de/filesys/archivefile.h \ + include/de/filesys/bytearrayfile.h \ include/de/filesys/directoryfeed.h \ include/de/filesys/feed.h \ include/de/filesys/file.h \ @@ -23,6 +25,7 @@ HEADERS += \ SOURCES += \ src/filesys/archivefeed.cpp \ src/filesys/archivefile.cpp \ + src/filesys/bytearrayfile.cpp \ src/filesys/directoryfeed.cpp \ src/filesys/feed.cpp \ src/filesys/file.cpp \ diff --git a/doomsday/libdeng2/include/de/ByteArrayFile b/doomsday/libdeng2/include/de/ByteArrayFile new file mode 100644 index 0000000000..ecfa60c626 --- /dev/null +++ b/doomsday/libdeng2/include/de/ByteArrayFile @@ -0,0 +1 @@ +#include "filesys/bytearrayfile.h" diff --git a/doomsday/libdeng2/include/de/IIOStream b/doomsday/libdeng2/include/de/IIOStream new file mode 100644 index 0000000000..f7c71884f5 --- /dev/null +++ b/doomsday/libdeng2/include/de/IIOStream @@ -0,0 +1 @@ +#include "data/iiostream.h" diff --git a/doomsday/libdeng2/include/de/data/block.h b/doomsday/libdeng2/include/de/data/block.h index 1cacf81f06..489e4255fe 100644 --- a/doomsday/libdeng2/include/de/data/block.h +++ b/doomsday/libdeng2/include/de/data/block.h @@ -27,8 +27,10 @@ namespace de { +class IIOStream; + /** - * Simple data buffer that implements the byte array interface. + * Data buffer that implements the byte array interface. * * @ingroup data */ @@ -40,6 +42,24 @@ class DENG2_PUBLIC Block : public QByteArray, public IByteArray, public IBlock Block(const Block& other); Block(const QByteArray& byteArray); + /** + * Constructs a block by reading the contents of an I/O stream. The block + * will contain all the data that is available immediately; will not wait + * for additional data to become available later. + * + * @param stream Stream to read from. + */ + Block(IIOStream& stream); + + /** + * Constructs a block by reading the contents of a I/O stream in const + * mode. The block will contain all the data that is available immediately; + * will not wait for additional data to become available later. + * + * @param stream Stream to read from. + */ + Block(const IIOStream& stream); + /** * Construct a new block and copy its contents from the specified * location at another array. diff --git a/doomsday/libdeng2/include/de/data/iiostream.h b/doomsday/libdeng2/include/de/data/iiostream.h new file mode 100644 index 0000000000..f2ebba169a --- /dev/null +++ b/doomsday/libdeng2/include/de/data/iiostream.h @@ -0,0 +1,90 @@ +/** + * @file iiostream.h + * I/O stream interface. @ingroup data + * + * @authors Copyright © 2012 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBDENG2_IIOSTREAM_H +#define LIBDENG2_IIOSTREAM_H + +#include "../IByteArray" + +namespace de { + +/** + * Interface that allows communication via an input/output stream of bytes. + * + * When reading from a stream, the stream can either be in modifiable + * (non-const) or immutable (const) mode. When reading bytes from a modifiable + * stream, the bytes are then removed from the stream and the next read will + * return a different set of bytes. Immutable streams, on the other hand, can + * be used for peeking ahead into the received data: the bytes are not removed + * from the stream during the read. + */ +class IIOStream +{ +public: + /// I/O to the stream failed. @ingroup errors + DENG2_ERROR(IOError); + + /// Only reading is allowed from the stream. @ingroup errors + DENG2_SUB_ERROR(IOError, ReadOnlyError); + +public: + virtual ~IIOStream() {} + + /** + * Writes an array of bytes to the stream. + * + * @param bytes Byte array to write. + * + * @return Reference to this IIOStream object. + */ + virtual IIOStream& operator << (const IByteArray& bytes) = 0; + + /** + * Reads all the available bytes into the array @a bytes. If there is + * nothing available for reading, nothing is written to @a bytes. The + * stream operates as a modifiable object: once read, the bytes are + * considered to be removed from the stream. + * + * @param bytes Read bytes are stored here. + * + * @return Reference to this IIOStream object. + */ + virtual IIOStream& operator >> (IByteArray& bytes) = 0; + + /** + * Reads all the available bytes into the array @a bytes. If there is + * nothing available for reading, nothing is written to @a bytes. + * + * Immutable streams can be used for peeking ahead into the received data. + * Here the stream operates as a const object: the read bytes are not + * removed from the stream, and a subsequent read will return the same + * bytes, plus any additional bytes that may have been produced in the + * meantime. + * + * @param bytes Read bytes are stored here. + * + * @return Reference to this IIOStream object. + */ + virtual const IIOStream& operator >> (IByteArray& bytes) const = 0; +}; + +} // namespace de + +#endif // LIBDENG2_IIOSTREAM_H diff --git a/doomsday/libdeng2/include/de/data/reader.h b/doomsday/libdeng2/include/de/data/reader.h index 4494bf10e5..46af9e2694 100644 --- a/doomsday/libdeng2/include/de/data/reader.h +++ b/doomsday/libdeng2/include/de/data/reader.h @@ -30,6 +30,7 @@ class Block; class String; class IReadable; class FixedByteArray; +class IIOStream; /** * Provides a protocol for reading data from a byte array object (anything with @@ -40,17 +41,36 @@ class FixedByteArray; */ class DENG2_PUBLIC Reader { +public: + /// Seeking is not possible, e.g., when reading from a stream. @ingroup errors + DENG2_ERROR(SeekError); + public: /** * Constructs a new reader. * * @param source Byte array containing the data to be read. - * @param byteOrder Byte order to use. The byte order defaults to network - * (big-endian) byte order. + * @param byteOrder Byte order to use. * @param offset Offset in @a source where to start reading. */ Reader(const IByteArray& source, const ByteOrder& byteOrder = littleEndianByteOrder, - IByteArray::Offset offset = 0); + IByteArray::Offset offset = 0); + + /** + * Constructs a new reader that reads from a stream. + * + * @param stream Stream where input is read. + * @param byteOrder Byte order to use. + */ + Reader(IIOStream& stream, const ByteOrder& byteOrder = littleEndianByteOrder); + + /** + * Constructs a new reader that reads from a const stream. + * + * @param stream Const stream where input is read. + * @param byteOrder Byte order to use. + */ + Reader(const IIOStream& stream, const ByteOrder& byteOrder = littleEndianByteOrder); //@{ Read a number from the source buffer, in network byte order. Reader& operator >> (char& byte); @@ -102,51 +122,52 @@ class DENG2_PUBLIC Reader /** * Returns the source byte array of the reader. */ - const IByteArray& source() const { - return _source; - } + const IByteArray* source() const; /** * Returns the offset used by the reader. */ - IByteArray::Offset offset() const { - return _offset; - } + IByteArray::Offset offset() const; /** * Move to a specific position in the source data. * * @param offset Offset to move to. */ - void setOffset(IByteArray::Offset offset) { - _offset = offset; - } + void setOffset(IByteArray::Offset offset); /** - * Moves the reader offset forward by a number of bytes. + * Moves the reader offset forward by a number of bytes. This is a random + * access seek: it is only possible if the source supports random access + * (e.g., it is impossible to seek in streams). * - * @param count Number of bytes to move forward. + * @param count Number of bytes to move forward. Negative values move + * the reader offset backward. */ void seek(dint count); /** - * Rewinds the read offset by a number of bytes. - * - * @param count Number of bytes to move backward. + * Marks the current position for rewinding later. After setting the mark, + * you are expected to call rewind() to return to the marked position. This + * method can be used even when reading from streams. + */ + void mark(); + + /** + * Rewinds the read offset to the mark set previously (using mark()). + * Rewinding can be done with all readers, regardless of where the data + * comes from. */ - void rewind(dint count); + void rewind(); /** * Returns the byte order of the writer. */ - const ByteOrder& byteOrder() const { - return _convert; - } + const ByteOrder& byteOrder() const; private: - const IByteArray& _source; - IByteArray::Offset _offset; - const ByteOrder& _convert; + struct Instance; + Instance* d; }; } // namespace de diff --git a/doomsday/libdeng2/include/de/data/writer.h b/doomsday/libdeng2/include/de/data/writer.h index 185270a702..efc07f0ac8 100644 --- a/doomsday/libdeng2/include/de/data/writer.h +++ b/doomsday/libdeng2/include/de/data/writer.h @@ -29,7 +29,9 @@ namespace de { class IWritable; class String; class Block; +class ByteArrayFile; class FixedByteArray; +class IIOStream; /** * Provides a protocol for writing data in a specific byte order into a byte @@ -40,6 +42,10 @@ class FixedByteArray; */ class DENG2_PUBLIC Writer { +public: + /// Seeking is not possible, e.g., when writing to a stream. @ingroup errors + DENG2_ERROR(SeekError); + public: /** * Constructs a new writer. @@ -59,6 +65,24 @@ class DENG2_PUBLIC Writer */ Writer(IByteArray& destination, IByteArray::Offset offset); + /** + * Constructs a new writer for writing to an I/O stream. + * + * @param stream Stream to write to. + * @param byteOrder Byte order to use. + */ + Writer(IIOStream& stream, const ByteOrder& byteOrder = littleEndianByteOrder); + + /** + * Constructs a new writer for writing into a byte array file. + * + * @param destination Byte array file to write to. + * @param byteOrder Byte order to use. + * @param offset Offset in @a destination where to start writing. + */ + Writer(ByteArrayFile& destination, const ByteOrder& byteOrder = littleEndianByteOrder, + IByteArray::Offset offset = 0); + /** * Constructs a new writer that uses the current offset of @a other as its * zero offset. @@ -68,6 +92,8 @@ class DENG2_PUBLIC Writer */ Writer(const Writer& other, const ByteOrder& byteOrder = littleEndianByteOrder); + virtual ~Writer(); + //@{ Write a number to the destination buffer, in the chosen byte order. Writer& operator << (const char& byte); Writer& operator << (const dchar& byte); @@ -117,47 +143,35 @@ class DENG2_PUBLIC Writer /** * Returns the destination byte array used by the writer. */ - const IByteArray& destination() const { - return _destination; - } + const IByteArray* destination() const; /** * Returns the destination byte array used by the writer. */ - IByteArray& destination() { - return _destination; - } + IByteArray* destination(); /** * Returns the offset used by the writer. */ - IByteArray::Offset offset() const { - return _offset; - } + IByteArray::Offset offset() const; - void setOffset(IByteArray::Offset offset) { - _offset = offset; - } + void setOffset(IByteArray::Offset offset); /** * Returns the byte order of the writer. */ - const ByteOrder& byteOrder() const { - return _convert; - } + const ByteOrder& byteOrder() const; /** * Moves the writer offset forward by a number of bytes. * - * @param count Number of bytes to move forward. + * @param count Number of bytes to move forward (negative to move backward). */ void seek(dint count); private: - IByteArray& _destination; - IByteArray::Offset _offset; - const IByteArray::Offset _fixedOffset; - const ByteOrder& _convert; + struct Instance; + Instance* d; }; } // namespace de diff --git a/doomsday/libdeng2/include/de/filesys/archivefeed.h b/doomsday/libdeng2/include/de/filesys/archivefeed.h index 9da319a935..7cb14c1ef2 100644 --- a/doomsday/libdeng2/include/de/filesys/archivefeed.h +++ b/doomsday/libdeng2/include/de/filesys/archivefeed.h @@ -21,6 +21,7 @@ #define LIBDENG2_ARCHIVEFEED_H #include "../Feed" +#include "../ByteArrayFile" #include "../String" namespace de @@ -34,6 +35,10 @@ namespace de */ class ArchiveFeed : public Feed { + public: + /// Provided source file cannot be used as a feed source. @ingroup errors + DENG2_ERROR(InvalidSourceError); + public: /** * Constructs a new archive feed. @@ -66,19 +71,11 @@ namespace de /** * Returns the base path within the archive. */ - const String& basePath() const { return _basePath; } + const String& basePath() const; private: - /// File where the archive is stored. - File& _file; - - Archive* _archive; - - /// Mount point within the archive for this feed. - String _basePath; - - /// The feed whose archive this feed is using. - ArchiveFeed* _parentFeed; + struct Instance; + Instance* d; }; } diff --git a/doomsday/libdeng2/include/de/filesys/archivefile.h b/doomsday/libdeng2/include/de/filesys/archivefile.h index ac32165ca4..bd18d7a8cc 100644 --- a/doomsday/libdeng2/include/de/filesys/archivefile.h +++ b/doomsday/libdeng2/include/de/filesys/archivefile.h @@ -20,8 +20,7 @@ #ifndef LIBDENG2_ARCHIVEFILE_H #define LIBDENG2_ARCHIVEFILE_H -#include "../File" -#include "../String" +#include "../ByteArrayFile" namespace de { @@ -32,7 +31,7 @@ namespace de * * @ingroup fs */ - class ArchiveFile : public File + class ArchiveFile : public ByteArrayFile { public: /** @@ -55,6 +54,7 @@ namespace de const Archive& archive() const { return _archive; } // Implements IByteArray. + Size size() const; void get(Offset at, Byte* values, Size count) const; void set(Offset at, const Byte* values, Size count); diff --git a/doomsday/libdeng2/include/de/filesys/bytearrayfile.h b/doomsday/libdeng2/include/de/filesys/bytearrayfile.h new file mode 100644 index 0000000000..aff982adef --- /dev/null +++ b/doomsday/libdeng2/include/de/filesys/bytearrayfile.h @@ -0,0 +1,53 @@ +/* + * The Doomsday Engine Project -- libdeng2 + * + * Copyright (c) 2012 Jaakko Keränen + * + * 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, see . + */ + +#ifndef LIBDENG2_BYTEARRAYFILE_H +#define LIBDENG2_BYTEARRAYFILE_H + +#include "../libdeng2.h" +#include "../File" +#include "../IByteArray" + +namespace de +{ + /** + * Reads from and writes to files that contain a random-access byte array + * of data. This is an abstract base class for files that have this + * property. + * + * When used as an I/O stream: reading from the stream outputs the entire + * contents of the file, and writing to the stream appends new content to + * the end of the file. + * + * @ingroup fs + */ + class DENG2_PUBLIC ByteArrayFile : public File, public IByteArray + { + protected: + ByteArrayFile(const String& name = "") : File(name) {} + + public: + // Implements IIOStream. + IIOStream& operator << (const IByteArray& bytes); + IIOStream& operator >> (IByteArray& bytes); + const IIOStream& operator >> (IByteArray& bytes) const; + }; +} + +#endif // LIBDENG2_BYTEARRAYFILE_H diff --git a/doomsday/libdeng2/include/de/filesys/file.h b/doomsday/libdeng2/include/de/filesys/file.h index de97dc7e04..6742ed44ef 100644 --- a/doomsday/libdeng2/include/de/filesys/file.h +++ b/doomsday/libdeng2/include/de/filesys/file.h @@ -20,7 +20,7 @@ #ifndef LIBDENG2_FILE_H #define LIBDENG2_FILE_H -#include "../IByteArray" +#include "../IIOStream" #include "../String" #include "../Time" #include "../Record" @@ -37,7 +37,15 @@ namespace de /** * Base class for all files stored in the file system. - * + * + * Implements the IIOStream interface to allow files to receive and send + * out a stream of bytes. The default implementation only throws an + * IIOStream::IOError -- it is up to subclasses to implement the stream in + * the context suitable for the concrete file class. + * + * Note that the constructor of File is protected: only subclasses can be + * instantiated. + * * Subclasses have some special requirements for their destructors: * - deindex() must be called in all subclass destructors so that the instances * indexed under the subclasses' type are removed from the file system's index also. @@ -47,15 +55,9 @@ namespace de * * @ingroup fs */ - class DENG2_PUBLIC File : public IByteArray + class DENG2_PUBLIC File : public IIOStream { - public: - /// I/O to the native file failed. @ingroup errors - DENG2_ERROR(IOError); - - /// Only reading is allowed from the file. @ingroup errors - DENG2_SUB_ERROR(IOError, ReadOnlyError); - + public: // Mode flags. enum Flag { @@ -245,6 +247,14 @@ namespace de */ const Status& status() const; + /** + * Returns the size of the file. + * + * @return Size in bytes. If the file does not have a size (purely + * stream-based file), the size is zero. + */ + dsize size() const; + /** * Forms the complete path of this file object. * @@ -278,11 +288,11 @@ namespace de */ void verifyWriteAccess(); - // Implements IByteArray. - Size size() const; - void get(Offset at, Byte* values, Size count) const; - void set(Offset at, const Byte* values, Size count); - + // Implements IIOStream. + IIOStream& operator << (const IByteArray& bytes); + IIOStream& operator >> (IByteArray& bytes); + const IIOStream& operator >> (IByteArray& bytes) const; + protected: /** * Constructs a new file. By default files are in read-only mode. diff --git a/doomsday/libdeng2/include/de/filesys/nativefile.h b/doomsday/libdeng2/include/de/filesys/nativefile.h index 6ca46597f0..a01249fba7 100644 --- a/doomsday/libdeng2/include/de/filesys/nativefile.h +++ b/doomsday/libdeng2/include/de/filesys/nativefile.h @@ -21,7 +21,7 @@ #define LIBDENG2_NATIVEFILE_H #include "../libdeng2.h" -#include "../File" +#include "../ByteArrayFile" #include "../NativePath" #include @@ -29,19 +29,20 @@ namespace de { /** - * Reads from and writes to files in the native file system. + * Reads from and writes to files in the native file system. The contents + * of the native file are available as a byte array. * * @ingroup fs */ - class DENG2_PUBLIC NativeFile : public File + class DENG2_PUBLIC NativeFile : public ByteArrayFile { public: /// Input from the native file failed. @ingroup errors - DENG2_SUB_ERROR(IOError, InputError); - + DENG2_SUB_ERROR(IOError, NativeInputError); + /// Output to the native file failed. @ingroup errors - DENG2_SUB_ERROR(IOError, OutputError); - + DENG2_SUB_ERROR(IOError, NativeOutputError); + public: /** * Constructs a NativeFile that accesses a file in the native file system diff --git a/doomsday/libdeng2/src/core/logbuffer.cpp b/doomsday/libdeng2/src/core/logbuffer.cpp index bfff23e40b..660564826d 100644 --- a/doomsday/libdeng2/src/core/logbuffer.cpp +++ b/doomsday/libdeng2/src/core/logbuffer.cpp @@ -156,27 +156,16 @@ class IOutputStream class FileOutputStream : public IOutputStream { public: - FileOutputStream(File* f) : _file(f), _writer(0) { - if(f) { - // New content is added to the end of the file. - _writer = new Writer(*_file, _file->size()); - } - } - ~FileOutputStream() { - if(_writer) { - delete _writer; - } - } + FileOutputStream(File* f) : _file(f) {} void flush() { if(_file) _file->flush(); } IOutputStream& operator << (const QString& text) { - if(_writer) (*_writer) << FixedByteArray(Block(text.toUtf8())); + if(_file) *_file << FixedByteArray(Block(text.toUtf8())); return *this; } private: File* _file; - Writer* _writer; }; /// Stream that outputs to a QTextStream. diff --git a/doomsday/libdeng2/src/data/archive.cpp b/doomsday/libdeng2/src/data/archive.cpp index 5921ea0c64..81e2d7ecbb 100644 --- a/doomsday/libdeng2/src/data/archive.cpp +++ b/doomsday/libdeng2/src/data/archive.cpp @@ -21,6 +21,7 @@ #include "de/ISerializable" #include "de/ByteSubArray" #include "de/FixedByteArray" +#include "de/ByteArrayFile" #include "de/Reader" #include "de/Writer" #include "de/LittleEndianByteOrder" diff --git a/doomsday/libdeng2/src/data/block.cpp b/doomsday/libdeng2/src/data/block.cpp index ba497e533f..eab7bd428e 100644 --- a/doomsday/libdeng2/src/data/block.cpp +++ b/doomsday/libdeng2/src/data/block.cpp @@ -18,6 +18,7 @@ */ #include "de/Block" +#include "de/File" using namespace de; @@ -40,6 +41,16 @@ Block::Block(const QByteArray& byteArray) : QByteArray(byteArray) {} +Block::Block(IIOStream& stream) +{ + stream >> *this; +} + +Block::Block(const IIOStream& stream) +{ + stream >> *this; +} + Block::Block(const IByteArray& other, Offset at, Size count) : IByteArray() { copyFrom(other, at, count); diff --git a/doomsday/libdeng2/src/data/reader.cpp b/doomsday/libdeng2/src/data/reader.cpp index 92ece9c7a2..7fc4598b74 100644 --- a/doomsday/libdeng2/src/data/reader.cpp +++ b/doomsday/libdeng2/src/data/reader.cpp @@ -21,15 +21,146 @@ #include "de/String" #include "de/Block" #include "de/ISerializable" +#include "de/IIOStream" #include "de/FixedByteArray" +#include "de/ByteRefArray" #include "de/data/byteorder.h" #include +#include -using namespace de; +namespace de { + +struct Reader::Instance +{ + const ByteOrder& convert; + + // Random access source: + const IByteArray* source; + IByteArray::Offset offset; + IByteArray::Offset markOffset; + + // Stream source: + IIOStream* stream; + const IIOStream* constStream; + dsize numReceivedBytes; + Block incoming; ///< Buffer for bytes received so far from the stream. + bool marking; ///< @c true, if marking is occurring (mark() called). + Block markedData; ///< All read data since the mark was set. + + Instance(const ByteOrder& order, const IByteArray* src, IByteArray::Offset off) + : convert(order), source(src), offset(off), markOffset(off), + stream(0), constStream(0), numReceivedBytes(0), marking(false) + {} + + Instance(const ByteOrder& order, IIOStream* str) + : convert(order), source(0), offset(0), markOffset(0), + stream(str), constStream(0), numReceivedBytes(0), marking(false) + {} + + Instance(const ByteOrder& order, const IIOStream* str) + : convert(order), source(0), offset(0), markOffset(0), + stream(0), constStream(str), numReceivedBytes(0), marking(false) + {} + + /** + * Reads bytes from the stream and adds them to the incoming buffer. + * + * @param expectedSize Number of bytes that the reader is expecting to read. + */ + void update(dsize expectedSize = 0) + { + if(incoming.size() >= expectedSize) + { + // No need to update yet. + return; + } + + if(stream) + { + // Modifiable stream: read new bytes, append accumulation. + Block b; + *stream >> b; + incoming += b; + } + else if(constStream) + { + Block b; + *constStream >> b; + // Immutable stream: append only bytes we haven't seen yet. + b.remove(0, numReceivedBytes); + incoming += b; + numReceivedBytes += b.size(); + } + } + + void readBytes(IByteArray::Byte* ptr, dsize size) + { + if(source) + { + source->get(offset, ptr, size); + offset += size; + } + else if(stream || constStream) + { + update(size); + if(incoming.size() >= size) + { + std::memcpy(ptr, incoming.constData(), size); + if(marking) + { + // We'll need this for rewinding a bit later. + markedData += incoming.left(size); + } + incoming.remove(0, size); + } + else + { + throw IIOStream::IOError("Reader::readBytes", + QString("Attempted to read %1 bytes from stream while only %2 " + "bytes are available").arg(size).arg(incoming.size())); + } + } + } + + void mark() + { + if(source) + { + markOffset = offset; + } + else + { + markedData.clear(); + marking = true; + } + } + + void rewind() + { + if(source) + { + offset = markOffset; + } + else + { + incoming.prepend(markedData); + markedData.clear(); + marking = false; + } + } +}; Reader::Reader(const IByteArray& source, const ByteOrder& byteOrder, IByteArray::Offset offset) - : _source(source), _offset(offset), _convert(byteOrder) + : d(new Instance(byteOrder, &source, offset)) +{} + +Reader::Reader(IIOStream& stream, const ByteOrder& byteOrder) + : d(new Instance(byteOrder, &stream)) +{} + +Reader::Reader(const IIOStream& stream, const ByteOrder& byteOrder) + : d(new Instance(byteOrder, &stream)) {} Reader& Reader::operator >> (char& byte) @@ -44,8 +175,7 @@ Reader& Reader::operator >> (dchar& byte) Reader& Reader::operator >> (duchar& byte) { - _source.get(_offset, &byte, 1); - ++_offset; + d->readBytes(&byte, 1); return *this; } @@ -56,9 +186,8 @@ Reader& Reader::operator >> (dint16& word) Reader& Reader::operator >> (duint16& word) { - _source.get(_offset, reinterpret_cast(&word), 2); - _offset += 2; - _convert.foreignToNative(word, word); + d->readBytes(reinterpret_cast(&word), 2); + d->convert.foreignToNative(word, word); return *this; } @@ -69,9 +198,8 @@ Reader& Reader::operator >> (dint32& dword) Reader& Reader::operator >> (duint32& dword) { - _source.get(_offset, reinterpret_cast(&dword), 4); - _offset += 4; - _convert.foreignToNative(dword, dword); + d->readBytes(reinterpret_cast(&dword), 4); + d->convert.foreignToNative(dword, dword); return *this; } @@ -82,9 +210,8 @@ Reader& Reader::operator >> (dint64& qword) Reader& Reader::operator >> (duint64& qword) { - _source.get(_offset, reinterpret_cast(&qword), 8); - _offset += 8; - _convert.foreignToNative(qword, qword); + d->readBytes(reinterpret_cast(&qword), 8); + d->convert.foreignToNative(qword, qword); return *this; } @@ -126,8 +253,7 @@ Reader& Reader::operator >> (IByteArray& byteArray) * a memory buffer where you can copy the contents directly. */ QScopedPointer data(new IByteArray::Byte[size]); - _source.get(_offset, data.data(), size); - _offset += size; + d->readBytes(data.data(), size); byteArray.set(0, data.data(), size); return *this; } @@ -141,8 +267,7 @@ Reader& Reader::operator >> (FixedByteArray& fixedByteArray) */ const dsize size = fixedByteArray.size(); QScopedPointer data(new IByteArray::Byte[size]); - _source.get(_offset, data.data(), size); - _offset += size; + d->readBytes(data.data(), size); fixedByteArray.set(0, data.data(), size); return *this; } @@ -152,8 +277,9 @@ Reader& Reader::operator >> (Block& block) duint size = 0; *this >> size; - block.copyFrom(_source, _offset, size); - _offset += size; + block.resize(size); + d->readBytes(block.data(), size); + return *this; } @@ -174,23 +300,48 @@ Reader& Reader::readUntil(IByteArray& byteArray, IByteArray::Byte delimiter) return *this; } +const IByteArray* Reader::source() const +{ + return d->source; +} + +IByteArray::Offset Reader::offset() const +{ + return d->offset; +} + +void Reader::setOffset(IByteArray::Offset offset) +{ + d->offset = offset; +} + void Reader::seek(dint count) { - if(IByteArray::Offset(_offset + count) >= _source.size()) + if(!d->source) + { + throw SeekError("Reader::seek", "Cannot seek when reading from a stream"); + } + + if(IByteArray::Offset(d->offset + count) >= d->source->size()) { throw IByteArray::OffsetError("Reader::seek", "Seek past bounds of source data"); } - _offset += count; + d->offset += count; } -void Reader::rewind(dint count) +void Reader::mark() { - if(IByteArray::Offset(_offset - count) >= _source.size()) - { - QString msg; - QTextStream os(&msg); - os << "(count: " << count << ", offset: " << _offset << ", size: " << _source.size() << ")"; - throw IByteArray::OffsetError("Reader::rewind", "Rewound past bounds of source data " + msg); - } - _offset -= count; + d->mark(); +} + +void Reader::rewind() +{ + d->rewind(); +} + +const ByteOrder& Reader::byteOrder() const +{ + return d->convert; } + +} // namespace de diff --git a/doomsday/libdeng2/src/data/value.cpp b/doomsday/libdeng2/src/data/value.cpp index 3e6ab0f8ee..b82110750b 100644 --- a/doomsday/libdeng2/src/data/value.cpp +++ b/doomsday/libdeng2/src/data/value.cpp @@ -163,8 +163,9 @@ void Value::call(Process& /*process*/, const Value& /*arguments*/) const Value* Value::constructFrom(Reader& reader) { SerialId id; + reader.mark(); reader >> id; - reader.rewind(sizeof(id)); + reader.rewind(); std::auto_ptr result; switch(id) diff --git a/doomsday/libdeng2/src/data/writer.cpp b/doomsday/libdeng2/src/data/writer.cpp index 95ed6cabcd..4fbf2691f6 100644 --- a/doomsday/libdeng2/src/data/writer.cpp +++ b/doomsday/libdeng2/src/data/writer.cpp @@ -21,43 +21,101 @@ #include "de/String" #include "de/Block" #include "de/ISerializable" +#include "de/IIOStream" #include "de/FixedByteArray" +#include "de/ByteRefArray" +#include "de/ByteArrayFile" #include "de/data/byteorder.h" #include -using namespace de; +namespace de { + +struct Writer::Instance +{ + const ByteOrder& convert; + IByteArray* destination; + IIOStream* stream; + IByteArray::Offset offset; + const IByteArray::Offset fixedOffset; + + Instance(const ByteOrder& order, IByteArray* dest, IByteArray::Offset off) + : convert(order), destination(dest), stream(0), offset(off), fixedOffset(0) {} + + Instance(const ByteOrder& order, IIOStream* str) + : convert(order), destination(0), stream(str), offset(0), fixedOffset(0) + { + destination = dynamic_cast(str); + if(destination) + { + // The object that implements the stream may also implement a byte + // array interface -- that provides us more freedom to write, so + // prefer to use it instead. + stream = 0; + } + } + + Instance(const Instance& other, const ByteOrder& order) + : convert (order ), + destination(other.destination), + stream (other.stream ), + offset (other.offset ), + fixedOffset(other.fixedOffset) + {} + + void write(const IByteArray::Byte* ptr, dsize size) + { + if(destination) + { + destination->set(fixedOffset + offset, ptr, size); + offset += size; + } + else if(stream) + { + *stream << ByteRefArray(ptr, size); + } + } +}; Writer::Writer(IByteArray& destination, const ByteOrder& byteOrder, IByteArray::Offset offset) - : _destination(destination), _offset(offset), _fixedOffset(0), _convert(byteOrder) + : d(new Instance(byteOrder, &destination, offset)) {} Writer::Writer(IByteArray& destination, IByteArray::Offset offset) - : _destination(destination), _offset(offset), _fixedOffset(0), _convert(littleEndianByteOrder) + : d(new Instance(littleEndianByteOrder, &destination, offset)) +{} + +Writer::Writer(IIOStream& stream, const ByteOrder& byteOrder) + : d(new Instance(byteOrder, &stream)) +{} + +Writer::Writer(ByteArrayFile& destination, const ByteOrder& byteOrder, IByteArray::Offset offset) + : d(new Instance(byteOrder, static_cast(&destination), offset)) {} Writer::Writer(const Writer& other, const ByteOrder& byteOrder) - : _destination(other._destination), _offset(0), - _fixedOffset(other._fixedOffset + other._offset), _convert(byteOrder) + : d(new Instance(*other.d, byteOrder)) {} +Writer::~Writer() +{ + delete d; +} + Writer& Writer::operator << (const char& byte) { - _destination.set(_fixedOffset + _offset, reinterpret_cast(&byte), 1); - ++_offset; + d->write(reinterpret_cast(&byte), 1); return *this; } Writer& Writer::operator << (const dchar& byte) { - _destination.set(_fixedOffset + _offset, reinterpret_cast(&byte), 1); - ++_offset; + d->write(reinterpret_cast(&byte), 1); return *this; } Writer& Writer::operator << (const duchar& byte) { - _destination.set(_fixedOffset + _offset, &byte, 1); - ++_offset; + d->write(&byte, 1); return *this; } @@ -69,9 +127,8 @@ Writer& Writer::operator << (const dint16& word) Writer& Writer::operator << (const duint16& word) { duint16 netWord; - _convert.nativeToForeign(word, netWord); - _destination.set(_fixedOffset + _offset, reinterpret_cast(&netWord), 2); - _offset += 2; + d->convert.nativeToForeign(word, netWord); + d->write(reinterpret_cast(&netWord), 2); return *this; } @@ -83,9 +140,8 @@ Writer& Writer::operator << (const dint32& dword) Writer& Writer::operator << (const duint32& dword) { duint32 netDword; - _convert.nativeToForeign(dword, netDword); - _destination.set(_fixedOffset + _offset, reinterpret_cast(&netDword), 4); - _offset += 4; + d->convert.nativeToForeign(dword, netDword); + d->write(reinterpret_cast(&netDword), 4); return *this; } @@ -97,9 +153,8 @@ Writer& Writer::operator << (const dint64& qword) Writer& Writer::operator << (const duint64& qword) { duint64 netQword; - _convert.nativeToForeign(qword, netQword); - _destination.set(_fixedOffset + _offset, reinterpret_cast(&netQword), 8); - _offset += 8; + d->convert.nativeToForeign(qword, netQword); + d->write(reinterpret_cast(&netQword), 8); return *this; } @@ -121,10 +176,7 @@ Writer& Writer::operator << (const String& text) duint size = bytes.size(); *this << size; - _destination.set(_fixedOffset + _offset, - bytes.data(), - size); - _offset += size; + d->write(bytes.data(), size); return *this; } @@ -146,8 +198,7 @@ Writer& Writer::operator << (const FixedByteArray& fixedByteArray) const dsize size = fixedByteArray.size(); QScopedPointer data(new IByteArray::Byte[size]); fixedByteArray.get(0, data.data(), size); - _destination.set(_fixedOffset + _offset, data.data(), size); - _offset += size; + d->write(data.data(), size); return *this; } @@ -157,8 +208,7 @@ Writer& Writer::operator << (const Block& block) duint size = block.size(); *this << size; - _destination.set(_fixedOffset + _offset, block.data(), size); - _offset += size; + d->write(block.data(), size); return *this; } @@ -168,11 +218,48 @@ Writer& Writer::operator << (const IWritable& writable) return *this; } +const IByteArray* Writer::destination() const +{ + return d->destination; +} + +IByteArray* Writer::destination() +{ + return d->destination; +} + +IByteArray::Offset Writer::offset() const +{ + return d->offset; +} + +void Writer::setOffset(IByteArray::Offset offset) +{ + if(d->stream) + { + throw SeekError("Writer::setOffset", "Cannot change offset when writing to a stream"); + } + + d->offset = offset; +} + +const ByteOrder& Writer::byteOrder() const +{ + return d->convert; +} + void Writer::seek(dint count) { - if(dint(_fixedOffset + _offset) + count < 0) + if(d->stream) + { + throw SeekError("Writer::seek", "Cannot seek when writing to a stream"); + } + + if(dint(d->fixedOffset + d->offset) + count < 0) { throw IByteArray::OffsetError("Writer::seek", "Seek past beginning of destination"); } - _offset += count; + d->offset += count; } + +} // namespace de diff --git a/doomsday/libdeng2/src/filesys/archivefeed.cpp b/doomsday/libdeng2/src/filesys/archivefeed.cpp index f2dd0702b6..49cc5a179b 100644 --- a/doomsday/libdeng2/src/filesys/archivefeed.cpp +++ b/doomsday/libdeng2/src/filesys/archivefeed.cpp @@ -19,108 +19,170 @@ #include "de/ArchiveFeed" #include "de/ArchiveFile" +#include "de/ByteArrayFile" #include "de/Archive" #include "de/Writer" #include "de/Folder" #include "de/FS" #include "de/Log" -using namespace de; +namespace de { -ArchiveFeed::ArchiveFeed(File& archiveFile) : _file(archiveFile), _archive(0), _parentFeed(0) +struct ArchiveFeed::Instance { - // Open the archive. - _archive = new Archive(archiveFile); -} + ArchiveFeed& self; -ArchiveFeed::ArchiveFeed(ArchiveFeed& parentFeed, const String& basePath) - : _file(parentFeed._file), _archive(0), _basePath(basePath), _parentFeed(&parentFeed) -{} + /// File where the archive is stored (in a serialized format). + File& file; -ArchiveFeed::~ArchiveFeed() -{ - LOG_AS("~ArchiveFeed"); - - if(_archive) - { - // If modified, the archive is written. - if(_archive->modified()) - { - LOG_MSG("Updating archive in ") << _file.name(); + /// The archive can be physically stored here, as Archive doesn't make a + /// copy of the buffer. + Block serializedArchive; + + Archive* arch; - // Make sure we have either a compressed or uncompressed version of - // each entry in memory before destroying the source file. - _archive->cache(); + /// Mount point within the archive for this feed. + String basePath; - _file.clear(); - Writer(_file) << *_archive; - } + /// The feed whose archive this feed is using. + ArchiveFeed* parentFeed; + + Instance(ArchiveFeed* feed, File& f) : self(*feed), file(f), arch(0), parentFeed(0) + { + // If the file happens to be a byte array file, we can use it + // directly to store the Archive. + IByteArray* bytes = dynamic_cast(&f); + + // Open the archive. + if(bytes) + { + arch = new Archive(*bytes); + } else { - LOG_VERBOSE("Not updating archive in %s (not changed)") << _file.name(); + // The file is just a stream, so we can't rely on the file + // acting as the physical storage location for Archive. + f >> serializedArchive; + arch = new Archive(serializedArchive); } - delete _archive; } -} -void ArchiveFeed::populate(Folder& folder) -{ - LOG_AS("ArchiveFeed::populate"); + Instance(ArchiveFeed* feed, ArchiveFeed& parentFeed, const String& path) + : self(*feed), file(parentFeed.d->file), arch(0), basePath(path), parentFeed(&parentFeed) + {} - Archive::Names names; - - // Get a list of the files in this directory. - archive().listFiles(names, _basePath); - - for(Archive::Names::iterator i = names.begin(); i != names.end(); ++i) + ~Instance() { - if(folder.has(*i)) + if(arch) { - // Already has an entry for this, skip it (wasn't pruned so it's OK). - return; + // If modified, the archive is written. + if(arch->modified()) + { + LOG_MSG("Updating archive in ") << file.name(); + + // Make sure we have either a compressed or uncompressed version of + // each entry in memory before destroying the source file. + arch->cache(); + + file.clear(); + Writer(file) << *arch; + } + else + { + LOG_VERBOSE("Not updating archive in %s (not changed)") << file.name(); + } + delete arch; } - - String entry = _basePath / *i; - - std::auto_ptr archFile(new ArchiveFile(*i, archive(), entry)); - // Use the status of the entry within the archive. - archFile->setStatus(archive().status(entry)); - - // Create a new file that accesses this feed's archive and interpret the contents. - File* file = folder.fileSystem().interpret(archFile.release()); - folder.add(file); - - // We will decide on pruning this. - file->setOriginFeed(this); - - // Include the file in the main index. - folder.fileSystem().index(*file); } - - // Also populate subfolders. - archive().listFolders(names, _basePath); - - for(Archive::Names::iterator i = names.begin(); i != names.end(); ++i) + + Archive& archive() { - String subBasePath = _basePath / *i; - Folder& subFolder = folder.fileSystem().makeFolder(folder.path() / *i); - - // Does it already have the appropriate feed? - for(Folder::Feeds::const_iterator i = subFolder.feeds().begin(); - i != subFolder.feeds().end(); ++i) + if(parentFeed) { - ArchiveFeed* archFeed = const_cast(dynamic_cast(*i)); - if(archFeed && &archFeed->archive() == &archive() && archFeed->basePath() == subBasePath) + return parentFeed->archive(); + } + return *arch; + } + + void populate(Folder& folder) + { + Archive::Names names; + + // Get a list of the files in this directory. + archive().listFiles(names, basePath); + + for(Archive::Names::iterator i = names.begin(); i != names.end(); ++i) + { + if(folder.has(*i)) { - // It's got it. - LOG_DEBUG("Feed for ") << archFeed->basePath() << " already there."; + // Already has an entry for this, skip it (wasn't pruned so it's OK). return; } + + String entry = basePath / *i; + + std::auto_ptr archFile(new ArchiveFile(*i, archive(), entry)); + // Use the status of the entry within the archive. + archFile->setStatus(archive().status(entry)); + + // Create a new file that accesses this feed's archive and interpret the contents. + File* file = folder.fileSystem().interpret(archFile.release()); + folder.add(file); + + // We will decide on pruning this. + file->setOriginFeed(&self); + + // Include the file in the main index. + folder.fileSystem().index(*file); + } + + // Also populate subfolders. + archive().listFolders(names, basePath); + + for(Archive::Names::iterator i = names.begin(); i != names.end(); ++i) + { + String subBasePath = basePath / *i; + Folder& subFolder = folder.fileSystem().makeFolder(folder.path() / *i); + + // Does it already have the appropriate feed? + for(Folder::Feeds::const_iterator i = subFolder.feeds().begin(); + i != subFolder.feeds().end(); ++i) + { + ArchiveFeed* archFeed = const_cast(dynamic_cast(*i)); + if(archFeed && &archFeed->archive() == &archive() && archFeed->basePath() == subBasePath) + { + // It's got it. + LOG_DEBUG("Feed for ") << archFeed->basePath() << " already there."; + return; + } + } + + // Create a new feed. + subFolder.attach(new ArchiveFeed(self, subBasePath)); } - - // Create a new feed. - subFolder.attach(new ArchiveFeed(*this, subBasePath)); } +}; + +ArchiveFeed::ArchiveFeed(File &archiveFile) + : d(new Instance(this, archiveFile)) +{} + +ArchiveFeed::ArchiveFeed(ArchiveFeed& parentFeed, const String& basePath) + : d(new Instance(this, parentFeed, basePath)) +{} + +ArchiveFeed::~ArchiveFeed() +{ + LOG_AS("~ArchiveFeed"); + + delete d; +} + +void ArchiveFeed::populate(Folder& folder) +{ + LOG_AS("ArchiveFeed::populate"); + + d->populate(folder); } bool ArchiveFeed::prune(File& /*file*/) const @@ -131,7 +193,7 @@ bool ArchiveFeed::prune(File& /*file*/) const File* ArchiveFeed::newFile(const String& name) { - String newEntry = _basePath / name; + String newEntry = d->basePath / name; if(archive().has(newEntry)) { /// @throw AlreadyExistsError The entry @a name already exists in the archive. @@ -146,15 +208,17 @@ File* ArchiveFeed::newFile(const String& name) void ArchiveFeed::removeFile(const String& name) { - String entryPath = _basePath / name; - archive().remove(entryPath); + archive().remove(d->basePath / name); } Archive& ArchiveFeed::archive() { - if(_parentFeed) - { - return _parentFeed->archive(); - } - return *_archive; + return d->archive(); } + +const String& ArchiveFeed::basePath() const +{ + return d->basePath; +} + +} // namespace de diff --git a/doomsday/libdeng2/src/filesys/archivefile.cpp b/doomsday/libdeng2/src/filesys/archivefile.cpp index c81f0ab1f2..e719931248 100644 --- a/doomsday/libdeng2/src/filesys/archivefile.cpp +++ b/doomsday/libdeng2/src/filesys/archivefile.cpp @@ -24,7 +24,7 @@ using namespace de; ArchiveFile::ArchiveFile(const String& name, Archive& archive, const String& entryPath) - : File(name), _archive(archive), _entryPath(entryPath) + : ByteArrayFile(name), _archive(archive), _entryPath(entryPath) {} ArchiveFile::~ArchiveFile() @@ -48,6 +48,11 @@ void ArchiveFile::clear() setStatus(st); } +IByteArray::Size ArchiveFile::size() const +{ + return archive().entryBlock(_entryPath).size(); +} + void ArchiveFile::get(Offset at, Byte* values, Size count) const { archive().entryBlock(_entryPath).get(at, values, count); diff --git a/doomsday/libdeng2/src/filesys/bytearrayfile.cpp b/doomsday/libdeng2/src/filesys/bytearrayfile.cpp new file mode 100644 index 0000000000..b9eadd6ba5 --- /dev/null +++ b/doomsday/libdeng2/src/filesys/bytearrayfile.cpp @@ -0,0 +1,50 @@ +/* + * The Doomsday Engine Project -- libdeng2 + * + * Copyright (c) 2012 Jaakko Keränen + * + * 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, see . + */ + +#include "de/ByteArrayFile" + +namespace de { + +IIOStream& ByteArrayFile::operator << (const IByteArray& bytes) +{ + // Append the bytes to the end of the file. + Block block(bytes); + set(File::size(), block.data(), block.size()); + return *this; +} + +IIOStream& ByteArrayFile::operator >> (IByteArray& bytes) +{ + // Read the entire contents of the file. + Block block(File::size()); + get(0, block.data(), block.size()); + bytes.set(0, block.data(), block.size()); + return *this; +} + +const IIOStream& ByteArrayFile::operator >> (IByteArray& bytes) const +{ + // Read the entire contents of the file. + Block block(File::size()); + get(0, block.data(), block.size()); + bytes.set(0, block.data(), block.size()); + return *this; +} + +} // namespace de diff --git a/doomsday/libdeng2/src/filesys/file.cpp b/doomsday/libdeng2/src/filesys/file.cpp index 6dcbb059ae..c140668ccf 100644 --- a/doomsday/libdeng2/src/filesys/file.cpp +++ b/doomsday/libdeng2/src/filesys/file.cpp @@ -32,12 +32,11 @@ File::File(const String& fileName) _source = this; // Create the default set of info variables common to all files. - _info.add(new Variable("name", new Accessor(*this, Accessor::NAME), Accessor::VARIABLE_MODE)); - _info.add(new Variable("path", new Accessor(*this, Accessor::PATH), Accessor::VARIABLE_MODE)); - _info.add(new Variable("type", new Accessor(*this, Accessor::TYPE), Accessor::VARIABLE_MODE)); - _info.add(new Variable("size", new Accessor(*this, Accessor::SIZE), Accessor::VARIABLE_MODE)); - _info.add(new Variable("modifiedAt", new Accessor(*this, Accessor::MODIFIED_AT), - Accessor::VARIABLE_MODE)); + _info.add(new Variable("name", new Accessor(*this, Accessor::NAME), Accessor::VARIABLE_MODE)); + _info.add(new Variable("path", new Accessor(*this, Accessor::PATH), Accessor::VARIABLE_MODE)); + _info.add(new Variable("type", new Accessor(*this, Accessor::TYPE), Accessor::VARIABLE_MODE)); + _info.add(new Variable("size", new Accessor(*this, Accessor::SIZE), Accessor::VARIABLE_MODE)); + _info.add(new Variable("modifiedAt", new Accessor(*this, Accessor::MODIFIED_AT), Accessor::VARIABLE_MODE)); } File::~File() @@ -172,22 +171,27 @@ void File::verifyWriteAccess() } } -File::Size File::size() const +IIOStream& File::operator << (const IByteArray& bytes) { - return status().size; + DENG2_UNUSED(bytes); + throw IOError("File::operator <<", "File does not access input"); } -void File::get(Offset at, Byte* /*values*/, Size count) const +IIOStream& File::operator >> (IByteArray& bytes) { - if(at >= size() || at + count > size()) - { - throw OffsetError("File::get", "Out of range"); - } + DENG2_UNUSED(bytes); + throw IOError("File::operator >>", "File does not produce output"); } -void File::set(Offset /*at*/, const Byte* /*values*/, Size /*count*/) +const IIOStream& File::operator >> (IByteArray& bytes) const { - verifyWriteAccess(); + DENG2_UNUSED(bytes); + throw IOError("File::operator >>", "File does not produce output"); +} + +dsize File::size() const +{ + return status().size; } File::Accessor::Accessor(File& owner, Property prop) : _owner(owner), _prop(prop) diff --git a/doomsday/libdeng2/src/filesys/fs.cpp b/doomsday/libdeng2/src/filesys/fs.cpp index 76a6f2e9c1..9ef32ccf17 100644 --- a/doomsday/libdeng2/src/filesys/fs.cpp +++ b/doomsday/libdeng2/src/filesys/fs.cpp @@ -99,8 +99,11 @@ File* FS::interpret(File* sourceData) // It is a ZIP archive. The folder will own the source file. std::auto_ptr zip(new Folder(sourceData->name())); - zip->setSource(sourceData); - zip->attach(new ArchiveFeed(*sourceData)); + // Give ownership of the source to the folder. + zip->setSource(sourceData); + sourceData = 0; + // Create the feed. + zip->attach(new ArchiveFeed(*zip->source())); return zip.release(); } } diff --git a/doomsday/libdeng2/src/filesys/nativefile.cpp b/doomsday/libdeng2/src/filesys/nativefile.cpp index bf5566dc35..eb4cb39f51 100644 --- a/doomsday/libdeng2/src/filesys/nativefile.cpp +++ b/doomsday/libdeng2/src/filesys/nativefile.cpp @@ -23,7 +23,7 @@ using namespace de; NativeFile::NativeFile(const String& name, const NativePath& nativePath) - : File(name), _nativePath(nativePath), _in(0), _out(0) + : ByteArrayFile(name), _nativePath(nativePath), _in(0), _out(0) {} NativeFile::~NativeFile() @@ -99,8 +99,9 @@ void NativeFile::set(Offset at, const Byte* values, Size count) out.write(reinterpret_cast(values), count); if(out.error() != QFile::NoError) { - throw OutputError("NativeFile::set", "Error writing to file:" + - out.errorString()); + /// @throw NativeOutputError Failure to write to the native file. + throw NativeOutputError("NativeFile::set", "Error writing to file:" + + out.errorString()); } // Update status. Status st = status(); @@ -125,8 +126,8 @@ QFile& NativeFile::input() const { delete _in; _in = 0; - /// @throw InputError Opening the input stream failed. - throw InputError("NativeFile::input", "Failed to read " + _nativePath); + /// @throw NativeInputError Opening the input stream failed. + throw NativeInputError("NativeFile::input", "Failed to read " + _nativePath); } } return *_in; @@ -149,8 +150,8 @@ QFile& NativeFile::output() { delete _out; _out = 0; - /// @throw OutputError Opening the output stream failed. - throw OutputError("NativeFile::output", "Failed to write " + _nativePath); + /// @throw NativeOutputError Opening the output stream failed. + throw NativeOutputError("NativeFile::output", "Failed to write " + _nativePath); } if(mode() & Truncate) { diff --git a/doomsday/libdeng2/src/net/packet.cpp b/doomsday/libdeng2/src/net/packet.cpp index 55bb92dc33..60f1684d68 100644 --- a/doomsday/libdeng2/src/net/packet.cpp +++ b/doomsday/libdeng2/src/net/packet.cpp @@ -63,8 +63,9 @@ void Packet::execute() const bool Packet::checkType(Reader& from, const String& type) { char ident[5]; + from.mark(); from >> ident[0] >> ident[1] >> ident[2] >> ident[3]; ident[4] = 0; - from.rewind(4); + from.rewind(); return !type.compareWithCase(ident); } diff --git a/doomsday/libdeng2/src/scriptsys/expression.cpp b/doomsday/libdeng2/src/scriptsys/expression.cpp index 2e3b15091e..25862c0b73 100644 --- a/doomsday/libdeng2/src/scriptsys/expression.cpp +++ b/doomsday/libdeng2/src/scriptsys/expression.cpp @@ -41,8 +41,9 @@ void Expression::push(Evaluator& evaluator, Record* names) const Expression* Expression::constructFrom(Reader& reader) { SerialId id; + reader.mark(); reader >> id; - reader.rewind(sizeof(id)); + reader.rewind(); std::auto_ptr result; switch(id) diff --git a/doomsday/libdeng2/src/scriptsys/script.cpp b/doomsday/libdeng2/src/scriptsys/script.cpp index a0a783afbe..3d2719f7ba 100644 --- a/doomsday/libdeng2/src/scriptsys/script.cpp +++ b/doomsday/libdeng2/src/scriptsys/script.cpp @@ -33,7 +33,7 @@ Script::Script(const String& source) Script::Script(const File& file) : _path(file.path()) { - Parser().parse(String::fromUtf8(file), *this); + Parser().parse(String::fromUtf8(Block(file)), *this); } Script::~Script() diff --git a/doomsday/libdeng2/src/scriptsys/statement.cpp b/doomsday/libdeng2/src/scriptsys/statement.cpp index 09ec779abb..e76699942f 100644 --- a/doomsday/libdeng2/src/scriptsys/statement.cpp +++ b/doomsday/libdeng2/src/scriptsys/statement.cpp @@ -35,8 +35,9 @@ using namespace de; Statement* Statement::constructFrom(Reader& reader) { SerialId id; + reader.mark(); reader >> id; - reader.rewind(sizeof(id)); + reader.rewind(); std::auto_ptr result; switch(id) diff --git a/doomsday/tests/archive/main.cpp b/doomsday/tests/archive/main.cpp index 6983829a33..2b5ee1842f 100644 --- a/doomsday/tests/archive/main.cpp +++ b/doomsday/tests/archive/main.cpp @@ -52,7 +52,7 @@ int main(int argc, char** argv) File::Status stats = hello.status(); LOG_MSG("hello.txt size: %i bytes, modified at %s") << stats.size << Date(stats.modifiedAt); - String content = String::fromUtf8(hello); + String content = String::fromUtf8(Block(hello)); LOG_MSG("The contents: \"%s\"") << content; try diff --git a/doomsday/tests/stringpool/main.cpp b/doomsday/tests/stringpool/main.cpp index 635ce27109..a96c625af3 100644 --- a/doomsday/tests/stringpool/main.cpp +++ b/doomsday/tests/stringpool/main.cpp @@ -87,7 +87,7 @@ int main(int, char**) // Serialize. Block b; Writer(b) << p; - qDebug() << "Serialized stringpool to " << b.size() << " bytes."; + qDebug() << "Serialized stringpool to" << b.size() << "bytes."; // Deserialize. StringPool p2;