From e1e1d7c1d9c72919b4f95fd9d49a4f22abf32a39 Mon Sep 17 00:00:00 2001 From: skyjake Date: Thu, 22 Nov 2012 08:42:27 +0200 Subject: [PATCH] Refactor|libdeng2: Files are I/O streams instead of byte arrays IIOStream is an interface that provides access to an object through a stream of bytes. Changed the interface of de::File, the base class for all files, from IByteArray to IIOStream. While many files are byte array based, there are also other kinds of files that behave like streams (cf. UNIX sockets, character devices). Added ByteArrayFile: an abstract base class for random-access byte array based files, where the I/O stream has a default implementation that accesses the file's contents (as a byte array). Block, Writer, and Reader constructors accept I/O streams as source/destination objects. Refactored Reader's rewind mechanism to work also with streams. Applied the pimpl idiom in Reader, Writer, and ArchiveFeed. Updated the rest of libdeng2 accordingly. --- doomsday/libdeng2/data.pri | 2 + doomsday/libdeng2/filesys.pri | 3 + doomsday/libdeng2/include/de/ByteArrayFile | 1 + doomsday/libdeng2/include/de/IIOStream | 1 + doomsday/libdeng2/include/de/data/block.h | 22 +- doomsday/libdeng2/include/de/data/iiostream.h | 90 +++++++ doomsday/libdeng2/include/de/data/reader.h | 69 ++++-- doomsday/libdeng2/include/de/data/writer.h | 54 +++-- .../libdeng2/include/de/filesys/archivefeed.h | 19 +- .../libdeng2/include/de/filesys/archivefile.h | 6 +- .../include/de/filesys/bytearrayfile.h | 53 ++++ doomsday/libdeng2/include/de/filesys/file.h | 40 ++-- .../libdeng2/include/de/filesys/nativefile.h | 15 +- doomsday/libdeng2/src/core/logbuffer.cpp | 15 +- doomsday/libdeng2/src/data/archive.cpp | 1 + doomsday/libdeng2/src/data/block.cpp | 11 + doomsday/libdeng2/src/data/reader.cpp | 211 +++++++++++++--- doomsday/libdeng2/src/data/value.cpp | 3 +- doomsday/libdeng2/src/data/writer.cpp | 147 +++++++++--- doomsday/libdeng2/src/filesys/archivefeed.cpp | 226 +++++++++++------- doomsday/libdeng2/src/filesys/archivefile.cpp | 7 +- .../libdeng2/src/filesys/bytearrayfile.cpp | 50 ++++ doomsday/libdeng2/src/filesys/file.cpp | 34 +-- doomsday/libdeng2/src/filesys/fs.cpp | 7 +- doomsday/libdeng2/src/filesys/nativefile.cpp | 15 +- doomsday/libdeng2/src/net/packet.cpp | 3 +- .../libdeng2/src/scriptsys/expression.cpp | 3 +- doomsday/libdeng2/src/scriptsys/script.cpp | 2 +- doomsday/libdeng2/src/scriptsys/statement.cpp | 3 +- doomsday/tests/archive/main.cpp | 2 +- doomsday/tests/stringpool/main.cpp | 2 +- 31 files changed, 850 insertions(+), 267 deletions(-) create mode 100644 doomsday/libdeng2/include/de/ByteArrayFile create mode 100644 doomsday/libdeng2/include/de/IIOStream create mode 100644 doomsday/libdeng2/include/de/data/iiostream.h create mode 100644 doomsday/libdeng2/include/de/filesys/bytearrayfile.h create mode 100644 doomsday/libdeng2/src/filesys/bytearrayfile.cpp 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;