From 71366d9a0773e8214d252e7c8c4a1e3830069fd1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 12:20:46 +0200 Subject: [PATCH] Added writing capability to streams. Created OutFileStream. --- stream/clients/ogre_datastream.hpp | 9 ++- stream/filters/pure_filter.hpp | 2 + stream/filters/slice_stream.hpp | 21 ++++++- stream/servers/outfile_stream.hpp | 41 +++++++++++++ stream/servers/std_ostream.hpp | 79 +++++++++++++++++++++++++ stream/servers/std_stream.hpp | 2 +- stream/stream.hpp | 14 ++++- stream/tests/.gitignore | 1 + stream/tests/Makefile | 5 +- stream/tests/file_write_test.cpp | 41 +++++++++++++ stream/tests/output/file_write_test.out | 12 ++++ vfs/clients/ogre_archive.hpp | 6 +- 12 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 stream/servers/outfile_stream.hpp create mode 100644 stream/servers/std_ostream.hpp create mode 100644 stream/tests/file_write_test.cpp create mode 100644 stream/tests/output/file_write_test.out diff --git a/stream/clients/ogre_datastream.hpp b/stream/clients/ogre_datastream.hpp index 7b4106001..76a6f20cf 100644 --- a/stream/clients/ogre_datastream.hpp +++ b/stream/clients/ogre_datastream.hpp @@ -23,6 +23,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Get the size, if possible if(inp->hasSize) mSize = inp->size(); + + // Allow writing if inp supports it + if(inp->isWritable) + mAccess |= Ogre::DataStream::WRITE; } public: @@ -37,7 +41,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Only implement the DataStream functions we have to implement size_t read(void *buf, size_t count) - { return inp->read(buf,count); } + { return inp->read(buf,count); } + + size_t write(const void *buf, size_t count) + { assert(inp->isWritable); return inp->write(buf,count); } void skip(long count) { diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index fe95e2075..ed1690d8c 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -23,12 +23,14 @@ class PureFilter : public Stream { src = _src; isSeekable = src->isSeekable; + isWritable = src->isWritable; hasPosition = src->hasPosition; hasSize = src->hasSize; hasPtr = src->hasPtr; } size_t read(void *buf, size_t count) { return src->read(buf, count); } + size_t write(const void *buf, size_t count) { return src->write(buf,count); } void seek(size_t pos) { src->seek(pos); } size_t tell() const { return src->tell(); } size_t size() const { return src->size(); } diff --git a/stream/filters/slice_stream.hpp b/stream/filters/slice_stream.hpp index 49f241cfc..20f202bdc 100644 --- a/stream/filters/slice_stream.hpp +++ b/stream/filters/slice_stream.hpp @@ -27,6 +27,7 @@ class SliceStream : public Stream hasPosition = true; hasSize = true; hasPtr = src->hasPtr; + isWritable = src->isWritable; } size_t read(void *buf, size_t count) @@ -35,7 +36,7 @@ class SliceStream : public Stream if(count > length-pos) count = length-pos; - // Seek into place and reading + // Seek into place and start reading src->seek(offset+pos); count = src->read(buf, count); @@ -44,6 +45,24 @@ class SliceStream : public Stream return count; } + // Note that writing to a slice does not allow you to append data, + // you may only overwrite existing data. + size_t write(const void *buf, size_t count) + { + assert(isWritable); + // Check that we're not reading past our slice + if(count > length-pos) + count = length-pos; + + // Seek into place and action + src->seek(offset+pos); + count = src->write(buf, count); + + pos += count; + assert(pos <= length); + return count; + } + void seek(size_t _pos) { pos = _pos; diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp new file mode 100644 index 000000000..37504d78c --- /dev/null +++ b/stream/servers/outfile_stream.hpp @@ -0,0 +1,41 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "std_ostream.hpp" +#include + +namespace Mangle { +namespace Stream { + +/** File stream based on std::ofstream, only supports writing. + */ +class OutFileStream : public StdOStream +{ + std::ofstream file; + + public: + /** + By default we overwrite the file. If append=true, then we will + open an existing file and seek to the end instead. + */ + OutFileStream(const std::string &name, bool append=false) + : StdOStream(&file) + { + std::ios::openmode mode = std::ios::binary; + if(append) + mode |= std::ios::app; + else + mode |= std::ios::trunc; + + file.open(name.c_str(), mode); + + if(file.fail()) + throw str_exception("OutFileStream: failed to open file " + name); + } + ~OutFileStream() { file.close(); } +}; + +typedef boost::shared_ptr OutFileStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp new file mode 100644 index 000000000..32999e8c1 --- /dev/null +++ b/stream/servers/std_ostream.hpp @@ -0,0 +1,79 @@ +#ifndef MANGLE_STREAM_STDIOSERVER_H +#define MANGLE_STREAM_STDIOSERVER_H + +#include "../stream.hpp" +#include +#include "../../tools/str_exception.hpp" + +namespace Mangle { +namespace Stream { + +/** Simple wrapper for std::ostream, only supports output. + */ +class StdOStream : public Stream +{ + std::ostream *inf; + + static void fail(const std::string &msg) + { throw str_exception("StdOStream: " + msg); } + + public: + StdOStream(std::ostream *_inf) + : inf(_inf) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + isWritable = true; + } + + size_t read(void*,size_t) + { + assert(0&&"reading not supported by StdOStream"); + } + + size_t write(const void* buf, size_t len) + { + inf->write((const char*)buf, len); + if(inf->fail()) + fail("error writing to stream"); + + // Unfortunately, stupid std::ostream doesn't have a pcount() to + // match gcount() for input. In general the std::iostream system + // is an idiotically designed stream library. + return len; + } + + void seek(size_t pos) + { + inf->seekp(pos); + if(inf->fail()) + fail("seek error"); + } + + size_t tell() const + // Hack around the fact that ifstream->tellp() isn't const + { return ((StdOStream*)this)->inf->tellp(); } + + size_t size() const + { + // Use the standard iostream size hack, terrible as it is. + std::streampos pos = inf->tellp(); + inf->seekp(0, std::ios::end); + size_t res = inf->tellp(); + inf->seekp(pos); + + if(inf->fail()) + fail("could not get stream size"); + + return res; + } + + bool eof() const + { return inf->eof(); } +}; + +typedef boost::shared_ptr StdOStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index bb8bb6cbc..448306e5c 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -8,7 +8,7 @@ namespace Mangle { namespace Stream { -/** Simplest wrapper for std::istream. +/** Simple wrapper for std::istream. */ class StdStream : public Stream { diff --git a/stream/stream.hpp b/stream/stream.hpp index a195fe321..bc369f6cd 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -23,13 +23,17 @@ class Stream /// If true, size() works bool hasSize; + /// If true, write() works. Writing through pointer operations is + /// not supported. + bool isWritable; + /// If true, the getPtr() functions work bool hasPtr; /// Initialize all bools to false by default Stream() : isSeekable(false), hasPosition(false), hasSize(false), - hasPtr(false) {} + isWritable(false), hasPtr(false) {} /// Virtual destructor virtual ~Stream() {} @@ -40,6 +44,14 @@ class Stream */ virtual size_t read(void* buf, size_t count) = 0; + /** Write a given number of bytes from the stream. Semantics is + similar to read(). Only valid if isWritable is true + + Since most implementations do NOT support writing we default to + an assert(0) here. + */ + virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } + /// Seek to an absolute position in this stream. Not all streams are /// seekable. virtual void seek(size_t pos) = 0; diff --git a/stream/tests/.gitignore b/stream/tests/.gitignore index 814490404..9dfd618e2 100644 --- a/stream/tests/.gitignore +++ b/stream/tests/.gitignore @@ -1 +1,2 @@ *_test +test.file diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 8504de741..924a252c3 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -15,6 +15,9 @@ audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_fi file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ +file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp + $(GCC) $< -o $@ + memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/file_write_test.cpp b/stream/tests/file_write_test.cpp new file mode 100644 index 000000000..c6c61fcca --- /dev/null +++ b/stream/tests/file_write_test.cpp @@ -0,0 +1,41 @@ +#include "../servers/outfile_stream.hpp" +#include + +using namespace Mangle::Stream; +using namespace std; + +void print(Stream &str) +{ + cout << "size=" << str.size() + << " pos=" << str.tell() + << " eof=" << str.eof() + << endl; +} + +int main() +{ + { + cout << "\nCreating file\n"; + OutFileStream out("test.file"); + print(out); + out.write("hello",5); + print(out); + } + + { + cout << "\nAppending to file\n"; + OutFileStream out("test.file", true); + print(out); + out.write(" again\n",7); + print(out); + } + + { + cout << "\nOverwriting file\n"; + OutFileStream out("test.file"); + print(out); + out.write("overwrite!\n",11); + print(out); + } + return 0; +} diff --git a/stream/tests/output/file_write_test.out b/stream/tests/output/file_write_test.out new file mode 100644 index 000000000..34b07c49f --- /dev/null +++ b/stream/tests/output/file_write_test.out @@ -0,0 +1,12 @@ + +Creating file +size=0 pos=0 eof=0 +size=5 pos=5 eof=0 + +Appending to file +size=5 pos=5 eof=0 +size=12 pos=12 eof=0 + +Overwriting file +size=0 pos=0 eof=0 +size=11 pos=11 eof=0 diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index a4986d7b9..77191881a 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -11,9 +11,9 @@ namespace VFS { /** An OGRE Archive implementation that wraps a Mangle::VFS filesystem. - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. + This has been built and tested against OGRE 1.6.2, and has been + extended for OGRE 1.7. You might have to make your own + modifications if you're working with newer (or older) versions. */ class MangleArchive : public Ogre::Archive {