diff --git a/examples/1_stream_known.cpp b/examples/1_stream_known.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67355d180ab8b340ad45a48584f1fd71e24e615f
--- /dev/null
+++ b/examples/1_stream_known.cpp
@@ -0,0 +1,26 @@
+
+#include <iostream>
+#include <sstream>
+#include <cassert>
+#include <tscpp.h>
+#include "types.h"
+
+using namespace std;
+using namespace tscpp;
+
+int main()
+{
+    //Serialize to buffer
+    Point3d p1(1,2,3);
+    stringstream ss;
+    OutputArchive oa(ss);
+    oa<<p1;
+    
+    //Unserialize from buffer
+    Point3d p2;
+    InputArchive ia(ss);
+    ia>>p2;
+    assert(p1==p2);
+    
+    cout<<"Test passed"<<endl;
+}
diff --git a/examples/Makefile b/examples/Makefile
index ca23e85127e5c2083d8c5a8ca58497579827254d..eb56345f9773054944548f2fbb8d3cdf4d66d02e 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -3,10 +3,12 @@ CXX = g++
 CXXFLAGS = -std=c++11 -O2 -Wall -I..
 
 all:
+	$(CXX) $(CXXFLAGS) 1_stream_known.cpp   ../tscpp.cpp -o 1_stream_known
 	$(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
 	./3_buffer_known
 	./4_buffer_unknown
 
 clean:
-	rm -f 3_buffer_known 4_buffer_unknown
+	rm -f 1_stream_known 3_buffer_known 4_buffer_unknown
diff --git a/tscpp.cpp b/tscpp.cpp
index 29634ef57cf3b0544da3b1845d33703dca3d1805..ae0ca42ba41228635e932ffcac1f2893f4c2499b 100644
--- a/tscpp.cpp
+++ b/tscpp.cpp
@@ -26,6 +26,7 @@
  ***************************************************************************/
 
 #include "tscpp.h"
+#include <memory>
 #if defined(__GNUC__) && !defined(_MIOSIX)
 #include <cxxabi.h>
 #endif
@@ -107,4 +108,40 @@ string demangle(const string& name)
     #endif
 }
 
+void OutputArchive::serializeImpl(const char *name, const void *data, int size)
+{
+    int nameSize=strlen(name);
+    os.write(name,nameSize+1);
+    os.write(reinterpret_cast<const char*>(data),size);
+}
+
+void InputArchive::unserializeImpl(const char *name, void *data, int size)
+{
+    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())
+    {
+        is.seekg(pos);
+        throw 1; //FIXME
+    }
+
+    if(memcmp(unserializedName.get(),name,nameSize+1))
+    {
+        is.seekg(pos);
+        throw 2; //FIXME
+    }
+
+    //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())
+    {
+        is.seekg(pos);
+        throw 3; //FIXME
+    }
+}
+
 } //namespace tscpp
diff --git a/tscpp.h b/tscpp.h
index 5e063225c53af1798eb179b487aa6706bb020607..d46b3489349e559b5434b1fce67e25989208f080 100644
--- a/tscpp.h
+++ b/tscpp.h
@@ -29,6 +29,8 @@
 
 #include <type_traits>
 #include <functional>
+#include <ostream>
+#include <istream>
 #include <cstring>
 #include <string>
 #include <map>
@@ -186,4 +188,88 @@ std::string peekTypeName(const void *buffer, int bufSize);
  */
 std::string demangle(const std::string& name);
 
+/**
+ * The output archive.
+ * This class allows to serialize objects to any ostream using the familiar
+ * << syntax
+ */
+class OutputArchive
+{
+public:
+    /**
+     * Constructor
+     * \param os ostream where srialized types will be written
+     */
+    OutputArchive(std::ostream& os) : os(os) {}
+
+    /**
+     * \internal
+     */
+    void serializeImpl(const char *name, const void *data, int size);
+
+private:
+    OutputArchive(const OutputArchive&)=delete;
+    OutputArchive& operator=(const OutputArchive&)=delete;
+
+    std::ostream& os;
+};
+
+/**
+ * Serialize a type
+ * \param oa archive where the type will be serialized
+ * \param t type to serialize
+ */
+template<typename T>
+OutputArchive& operator<<(OutputArchive& oa, const T& t)
+{
+    #ifndef _MIOSIX
+    static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
+    #endif
+    oa.serializeImpl(typeid(t).name(),&t,sizeof(t));
+    return oa;
+}
+
+/**
+ * The input archive.
+ * This class allows to unserialize types from a stream, as long as you know
+ * what types have been serialized in which order. Otherwise have a look at
+ * UnknownInputArchive.
+ * To unserialize, use the familiar >> syntax.
+ */
+class InputArchive
+{
+public:
+    /**
+     * Constructor
+     * \param os ostream where srialized types will be written
+     */
+    InputArchive(std::istream& is) : is(is) {}
+
+    /**
+     * \internal
+     */
+    void unserializeImpl(const char *name, void *data, int size);
+
+private:
+    InputArchive(const InputArchive&)=delete;
+    InputArchive& operator=(const InputArchive&)=delete;
+
+    std::istream& is;
+};
+
+/**
+ * Unserialize a type
+ * \param ia archive where the type has been serialized
+ * \param t type to unserialize
+ */
+template<typename T>
+InputArchive& operator>>(InputArchive& ia, T& t)
+{
+    #ifndef _MIOSIX
+    static_assert(std::is_trivially_copyable<T>::value,"Type is not trivially copyable");
+    #endif
+    ia.unserializeImpl(typeid(t).name(),&t,sizeof(t));
+    return ia;
+}
+
 } // namespace tscpp