diff --git a/examples/2_stream_unknown.cpp b/examples/2_stream_unknown.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ec5340585e0ccb5338720564c4327921d8064ab --- /dev/null +++ b/examples/2_stream_unknown.cpp @@ -0,0 +1,50 @@ + +#include <iostream> +#include <sstream> +#include <cassert> +#include <cstdlib> +#include <tscpp.h> +#include "types.h" + +using namespace std; +using namespace tscpp; + +int main() +{ + //Declare some types + Point2d p2d(1,2); + Point3d p3d(3,4,5); + MiscData md(p2d,p3d,12,-1); + + //Prepare a type pool for unserialization + TypePool tp; + tp.registerType<Point2d>([&](Point2d& t) + { + cout<<"Found a Point2d"<<endl; + assert(t==p2d); + }); + tp.registerType<Point3d>([&](Point3d& t) + { + cout<<"Found a Point3d"<<endl; + assert(t==p3d); + }); + tp.registerType<MiscData>([&](MiscData& t) + { + cout<<"Found a MiscData"<<endl; + assert(t==md); + }); + + //Serialize to buffer + stringstream ss; + OutputArchive oa(ss); + + oa<<p2d<<p3d<<md; + + //Unserialize from buffer + UnknownInputArchive ia(ss,tp); + ia.unserialize(); + ia.unserialize(); + ia.unserialize(); + + cout<<"Test passed"<<endl; +} diff --git a/examples/Makefile b/examples/Makefile index eb56345f9773054944548f2fbb8d3cdf4d66d02e..5dc24567ba941c1cbe8e04970adfc86001cb3ef7 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -4,11 +4,13 @@ CXXFLAGS = -std=c++11 -O2 -Wall -I.. all: $(CXX) $(CXXFLAGS) 1_stream_known.cpp ../tscpp.cpp -o 1_stream_known + $(CXX) $(CXXFLAGS) 2_stream_unknown.cpp ../tscpp.cpp -o 2_stream_unknown $(CXX) $(CXXFLAGS) 3_buffer_known.cpp ../tscpp.cpp -o 3_buffer_known $(CXX) $(CXXFLAGS) 4_buffer_unknown.cpp ../tscpp.cpp -o 4_buffer_unknown ./1_stream_known + ./2_stream_unknown ./3_buffer_known ./4_buffer_unknown clean: - rm -f 1_stream_known 3_buffer_known 4_buffer_unknown + rm -f 1_stream_known 2_stream_unknown 3_buffer_known 4_buffer_unknown diff --git a/tscpp.cpp b/tscpp.cpp index a058fcb27562127d4ec9c592a460eca207eead3a..b078a8c7437f2362719aa73c70803a7c824ed4de 100644 --- a/tscpp.cpp +++ b/tscpp.cpp @@ -46,6 +46,20 @@ int TypePool::unserializeUnknownImpl(const char *name, const void *buffer, int b return it->second.size; } +void TypePool::unserializeUnknownImpl(const string& name, istream& is, streampos pos) const +{ + auto it=types.find(name); + if(it==types.end()) { + is.seekg(pos); + throw TscppException("unknown type",name); + } + + unique_ptr<char[]> unserialized(new char[it->second.size]); + is.read(reinterpret_cast<char*>(unserialized.get()),it->second.size); + if(is.eof()) throw TscppException("eof"); + it->second.usc(unserialized.get()); +} + int serializeImpl(void *buffer, int bufSize, const char *name, const void *data, int size) { int nameSize=strlen(name); @@ -117,32 +131,42 @@ void OutputArchive::serializeImpl(const char *name, const void *data, int size) void InputArchive::unserializeImpl(const char *name, void *data, int size) { - pos=is.tellg(); + auto pos=is.tellg(); int nameSize=strlen(name); unique_ptr<char[]> unserializedName(new char[nameSize+1]); is.read(unserializedName.get(),nameSize+1); - if(is.eof()) errorImpl("eof"); + if(is.eof()) throw TscppException("eof"); - if(memcmp(unserializedName.get(),name,nameSize+1)) - errorImpl("wrong type found",true); + if(memcmp(unserializedName.get(),name,nameSize+1)) wrongType(pos); //NOTE: we are writing on top of a constructed type without calling its //destructor. However, since it is trivially copyable, we at least aren't //overwriting pointers to allocated memory. is.read(reinterpret_cast<char*>(data),size); - if(is.eof()) errorImpl("eof"); + if(is.eof()) throw TscppException("eof"); } -void InputArchive::errorImpl(const string& errorStr, bool printName) +void InputArchive::wrongType(streampos pos) { is.seekg(pos); - if(printName==false) throw TscppException(errorStr); - else { - string type; - getline(is,type,'\0'); + string name; + getline(is,name,'\0'); + is.seekg(pos); + throw TscppException("wrong type",name); +} + +void UnknownInputArchive::unserialize() +{ + auto pos=is.tellg(); + string name; + getline(is,name,'\0'); + if(is.eof()) + { is.seekg(pos); - throw TscppException(errorStr,type); + throw TscppException("eof"); } + + tp.unserializeUnknownImpl(name,is,pos); } } //namespace tscpp diff --git a/tscpp.h b/tscpp.h index f3b9d67139fbc25d9cec03d770e86c6fdf6d55be..9b5070292da56cdeb2038c7c1896ac342666ec81 100644 --- a/tscpp.h +++ b/tscpp.h @@ -97,6 +97,11 @@ public: */ int unserializeUnknownImpl(const char *name, const void *buffer, int bufSize) const; + /** + * \internal + */ + void unserializeUnknownImpl(const std::string& name, std::istream& is, std::streampos pos) const; + private: std::map<std::string,UnserializerImpl> types; ///< Registered types }; @@ -250,18 +255,17 @@ public: * \internal */ void unserializeImpl(const char *name, void *data, int size); - - /** - * \internal - */ - void errorImpl(const std::string& errorStr, bool printName=false); private: InputArchive(const InputArchive&)=delete; InputArchive& operator=(const InputArchive&)=delete; + + /** + * \internal + */ + void wrongType(std::streampos pos); std::istream& is; - std::streampos pos; }; /** @@ -281,6 +285,36 @@ InputArchive& operator>>(InputArchive& ia, T& t) return ia; } +/** + * The unknown input archive. + * This class allows to unserialize types from a stream which have been + * serialized in an unknown order. + */ +class UnknownInputArchive +{ +public: + /** + * Constructor + * \param os ostream where srialized types will be written + */ + UnknownInputArchive(std::istream& is, const TypePool& tp) : is(is), tp(tp) {} + + /** + * Unserialize one type from the input stream, calling the corresponding + * callback in the TypePool + * \throws TscppException if the type found in the stream has not been + * registred in the TypePool or if the stream eof is found + */ + void unserialize(); + +private: + UnknownInputArchive(const UnknownInputArchive&)=delete; + UnknownInputArchive& operator=(const UnknownInputArchive&)=delete; + + std::istream& is; + const TypePool& tp; +}; + /** * Exception class thrown by the input archives */ @@ -295,8 +329,8 @@ public: /** * \internal */ - TscppException(const std::string& what, const std::string& t) - : runtime_error(what), t(t) {} + TscppException(const std::string& what, const std::string& n) + : runtime_error(what), n(n) {} /** * If the exception is thrown because an unknown/unexpected type has been @@ -310,16 +344,16 @@ public: * try { * ia>>f; * } catch(TscppException& ex) { - * if(ex.type().empty()==false) - * cerr<<"While unserializing Foo, "<<demangle(ex.type())<<" was found\n"; + * if(ex.name().empty()==false) + * cerr<<"While unserializing Foo, "<<demangle(ex.name())<<" was found\n"; * } * \endcode * \return the serialized type name, or "" if eof was found */ - std::string type() const { return t; } + std::string name() const { return n; } private: - std::string t; + std::string n; }; } // namespace tscpp