changeset 292:30a969b658c1

MFD
author David Demelier <markand@malikania.fr>
date Thu, 13 Nov 2014 13:55:36 +0100
parents 8847c0acd944 (current diff) f4fc723429fe (diff)
children 8af39c019eb6
files
diffstat 4 files changed, 789 insertions(+), 209 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Pack.cpp	Thu Nov 13 13:23:53 2014 +0100
+++ b/C++/Pack.cpp	Thu Nov 13 13:55:36 2014 +0100
@@ -31,3 +31,85 @@
 } // !namespace
 
 const Pack::Endian Pack::mode = checkMode();
+
+/* --------------------------------------------------------
+ * PackReader
+ * -------------------------------------------------------- */
+
+PackReader::PackReader(Pack::Endian endian)
+	: m_endian(endian)
+{
+}
+
+/* --------------------------------------------------------
+ * PackFileReader
+ * -------------------------------------------------------- */
+
+PackFileReader::PackFileReader(const std::string &path, Pack::Endian endian)
+	: PackReader(endian)
+{
+	m_in.open(path, std::ifstream::in);
+}
+
+std::istream &PackFileReader::stream()
+{
+	return m_in;
+}
+
+/* --------------------------------------------------------
+ * PackStringReader
+ * -------------------------------------------------------- */
+
+PackStringReader::PackStringReader(std::string input, Pack::Endian endian)
+	: PackReader(endian)
+	, m_in(std::move(input))
+{
+}
+
+std::istream &PackStringReader::stream()
+{
+	return m_in;
+}
+
+/* --------------------------------------------------------
+ * PackWriter
+ * -------------------------------------------------------- */
+
+PackWriter::PackWriter(Pack::Endian endian)
+	: m_endian(endian)
+{
+}
+
+/* --------------------------------------------------------
+ * PackFileWriter
+ * -------------------------------------------------------- */
+
+PackFileWriter::PackFileWriter(const std::string &path, Pack::Endian endian)
+	: PackWriter(endian)
+{
+	m_out.open(path, std::ofstream::out);
+}
+
+std::ostream &PackFileWriter::stream()
+{
+	return m_out;
+}
+
+/* --------------------------------------------------------
+ * PackStringWriter
+ * -------------------------------------------------------- */
+
+PackStringWriter::PackStringWriter(Pack::Endian endian)
+	: PackWriter(endian)
+{
+}
+
+std::ostream &PackStringWriter::stream()
+{
+	return m_out;
+}
+
+std::string PackStringWriter::buffer() const
+{
+	return m_out.str();
+}
--- a/C++/Pack.h	Thu Nov 13 13:23:53 2014 +0100
+++ b/C++/Pack.h	Thu Nov 13 13:55:36 2014 +0100
@@ -21,7 +21,8 @@
 
 #include <cstdint>
 #include <fstream>
-#include <stdexcept>
+#include <memory>
+#include <sstream>
 #include <string>
 
 /**
@@ -36,6 +37,25 @@
  *	uint64_t
  */
 class Pack {
+private:
+	template <typename T>
+	struct IsContainer
+	{
+		using Yes	= char [1];
+		using No	= char [2];
+
+		template <typename U>
+		static Yes &test(typename U::value_type *);
+
+		template <typename U>
+		static No &test(...);
+
+		static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes);
+	};
+
+	friend class PackWriter;
+	friend class PackReader;
+
 public:
 	/**
 	 * @enum Endian
@@ -60,146 +80,311 @@
 	 */
 	template <typename T>
 	struct TypeInfo {
-		static const bool supported = false;
+		static constexpr const bool convertible{false};
+		static constexpr const bool serializable{false};
+	};
+
+	/**
+	 * Helper to mark a specialization convertible.
+	 *
+	 * Already done for:
+	 *	uint8_t
+	 *	uint16_t
+	 *	uint32_t
+	 *	uint64_t
+	 *
+	 * The specialization must have the following function:
+	 *
+	 *	static constexpr void convert(T &value) noexcept
+	 */
+	struct Convertible {
+		static constexpr const bool convertible{true};
+	};
+
+	/**
+	 * Helper to mark a specialization serializable.
+	 *
+	 * The specialisation must have the following functions:
+	 *
+	 *	static void serialize(PackWriter &writer, const T &)
+	 *	static void unserialize(PackReader &reader, T &)
+	 */
+	struct Serializable {
+		static constexpr const bool serializable{true};
 	};
 
 	/**
-	 * Convert data only if the requested endian is different.
+	 * Convert data inplace.
 	 *
-	 * @param value the value to convert if needed
-	 * @param endian the endian mode
-	 * @return the converted value
+	 * @param value the value
 	 */
 	template <typename T>
-	static inline T convert(T value, Endian endian)
+	static inline void convert(T &value) noexcept
 	{
-		static_assert(TypeInfo<T>::supported, "unsupported type");
+		static_assert(TypeInfo<T>::convertible, "unsupported type");
+
+		TypeInfo<T>::convert(value);
+	}
+};
+
+/**
+ * @class PackReader
+ * @brief Base abstract reader class
+ */
+class PackReader {
+protected:
+	Pack::Endian m_endian;
+
+	PackReader(Pack::Endian endian);
+
+	virtual std::istream &stream() = 0;
 
-		if (endian != mode)
-			return TypeInfo<T>::convert(value);
+public:
+	/**
+	 * Default destructor.
+	 */
+	virtual ~PackReader() = default;
 
-		return value;
+	/**
+	 * Read a primitive convertible type.
+	 *
+	 * @param value the value destination
+	 * @return *this
+	 */
+	template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr>
+	PackReader &operator>>(T &value)
+	{
+		stream().read(reinterpret_cast<char *>(&value), sizeof (T));
+
+		if (m_endian != Pack::mode)
+			Pack::convert(value);
+				
+		return *this;
 	}
 
 	/**
-	 * Write nothing, stop recursion.
-	 */
-	static void write(std::ostream &, Endian)
-	{
-	}
-
-	/**
-	 * Write binary data to the stream.
+	 * Read a serializable type.
 	 *
-	 * @param out the output stream
-	 * @param endian the endian mode
-	 * @param args the arguments
-	 * @throw std::runtime_exception on error
+	 * @param value the value destination
+	 * @return *this
 	 */
-	template <typename T, typename... Args>
-	static void write(std::ostream &out, Endian endian, const T &value, const Args&... args)
+	template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr>
+	PackReader &operator>>(T &value)
 	{
-		auto ret = convert(value, endian);
-		out.write(reinterpret_cast<const char *>(&ret), TypeInfo<T>::size);
-		write(out, endian, args...);
-	}
+		Pack::TypeInfo<T>::unserialize(*this, value);
 
-	/**
-	 * Read nothing, stop recursion.
-	 */
-	static void read(std::istream &, Endian)
-	{
+		return *this;
 	}
 
 	/**
-	 * Read binary data from the stream.
+	 * Read an array.
+	 *
+	 * This operator is a little bit tricky because you don't  know in
+	 * advance how much data you want to read. Because of that, this
+	 * function looks the capacity of the container and reads that number
+	 * of data.
 	 *
-	 * @param in the input stream
-	 * @param endian the endian mode
-	 * @param args the arguments
-	 * @throw std::runtime_exception on error
+	 * Because it looks for capacity, you can't use a container which
+	 * already have some data, they will be overriden.
+	 *
+	 * If this is a concern, you should roll your own loop to fill up
+	 * your container.
+	 *
+	 * @param container the container (all previous data will be lost)
+	 * @return *this
 	 */
-	template <typename T, typename... Args>
-	static void read(std::istream &in, Endian endian, T &value, Args&... args)
+	template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr>
+	PackReader &operator>>(T &container)
 	{
-		in.read(reinterpret_cast<char *>(&value), TypeInfo<T>::size);
-		value = convert(value, endian);
-		read(in, endian, args...);
+		typename T::value_type v;
+
+		T copy;
+
+		for (size_t i = 0; i < container.capacity(); ++i) {
+			(*this) >> v;
+			copy.push_back(v);
+		}
+
+		container = std::move(copy);
+
+		return *this;
+	}
+};
+
+/**
+ * @class PackWriter
+ * @brief Base abstract writer class
+ */
+class PackWriter {
+protected:
+	Pack::Endian m_endian;
+
+	PackWriter(Pack::Endian endian);
+
+	virtual std::ostream &stream() = 0;
+
+public:
+	/**
+	 * Default destructor.
+	 */
+	virtual ~PackWriter() = default;
+
+	/**
+	 * Write a convertible type to the stream.
+	 *
+	 * @param value the value
+	 * @return *this
+	 */
+	template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr>
+	PackWriter &operator<<(T value)
+	{
+		if (m_endian != Pack::mode)
+			Pack::convert(value);
+
+		stream().write(reinterpret_cast<const char *>(&value), sizeof (T));
+				
+		return *this;
+	}
+
+	/**
+	 * Write a serializable type to the stream.
+	 *
+	 * @param value the value
+	 * @return *this
+	 */
+	template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr>
+	PackWriter &operator<<(const T &value)
+	{
+		Pack::TypeInfo<T>::serialize(*this, value);
+
+		return *this;
 	}
 
 	/**
-	 * Read array and store it to the output iterator. Because the container
-	 * is not allocated, we use an integer based size to determine the
-	 * number of bytes to read.
+	 * Write a container to the stream.
 	 *
-	 * @param in the input stream
-	 * @param endian the endian mode
-	 * @param out the output it
-	 * @param count the number of bytes to read
-	 * @throw std::runtime_error on error
+	 * @param container the container
+	 * @return *this
+	 */
+	template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr>
+	PackWriter &operator<<(const T &container)
+	{
+		for (const auto &v : container)
+			(*this) << v;
+
+		return *this;
+	}
+};
+
+/**
+ * @class PackFileReader
+ * @brief Extract binary data from a file
+ */
+class PackFileReader : public PackReader {
+private:
+	std::ifstream m_in;
+
+protected:
+	std::istream &stream() override;
+
+public:
+	/**
+	 * Read a file.
+	 *
+	 * @param path the path
+	 * @param endian the endian requested
 	 */
-	template <typename OutputIt>
-	static void readArray(std::istream &in, Endian endian, OutputIt out, unsigned count)
-	{
-		typename OutputIt::container_type::value_type byte;
+	PackFileReader(const std::string &path, Pack::Endian endian);
+};
+
+/**
+ * @class PackStringReader
+ * @brief Extract binary data from a string
+ */
+class PackStringReader : public PackReader {
+private:
+	std::istringstream m_in;
+
+	std::istream &stream() override;
+
+public:
+	/**
+	 * Read a string.
+	 *
+	 * @param input the input string
+	 * @param endian the endian requested
+	 */
+	PackStringReader(std::string input, Pack::Endian endian);
+};
+
+/**
+ * @class PackFileWriter
+ * @brief Write binary data to a string
+ */
+class PackFileWriter : public PackWriter {
+private:
+	std::ofstream m_out;
 
-		for (unsigned i = 0; i < count; ++i) {
-			read(in, endian, byte);
-			*out++ = byte;
-		}
-	}
+protected:
+	std::ostream &stream() override;
+
+public:
+	/**
+	 * Write to a file.
+	 *
+	 * @param path the path
+	 * @param endian the endian requested
+	 */
+	PackFileWriter(const std::string &path, Pack::Endian endian);
+};
+
+/**
+ * @class PackStringWriter
+ * @brief Write binary data to a string
+ */
+class PackStringWriter : public PackWriter {
+private:
+	std::ostringstream m_out;
+
+	std::ostream &stream() override;
+
+public:
+	/**
+	 * Write to a string
+	 *
+	 * @param endian the endian requested
+	 */
+	PackStringWriter(Pack::Endian endian);
 
 	/**
-	 * Read a container from an input iterator and write it to output stream.
+	 * The current buffer. Returns a copy of the string.
 	 *
-	 * @param out the output stream
-	 * @param endian the endian mode
-	 * @param in the input iterator
-	 * @param count the number of bytes to write
-	 * @throw std::runtime_error on error
+	 * @return the string
 	 */
-	template <typename InputIt>
-	static void writeArray(std::ostream &out, Endian endian, InputIt in, unsigned count)
-	{
-		typename std::iterator_traits<InputIt>::value_type byte;
+	std::string buffer() const;
+};
 
-		for (unsigned i = 0; i < count; ++i) {
-			byte = *in++;
-			write(out, endian, byte);
-		}
+template <>
+struct Pack::TypeInfo<uint8_t> : public Pack::Convertible {
+	static constexpr void convert(uint8_t &) noexcept
+	{
+		// uint8_t are endian independent
 	}
 };
 
 template <>
-struct Pack::TypeInfo<uint8_t> {
-	static constexpr const bool supported = true;
-	static constexpr const size_t size = sizeof (uint8_t);
-
-	static constexpr uint8_t convert(uint8_t v)
+struct Pack::TypeInfo<uint16_t> : public Pack::Convertible {
+	static constexpr void convert(uint16_t &v)
 	{
-		return v;
+		v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L));
 	}
 };
 
 template <>
-struct Pack::TypeInfo<uint16_t> {
-	static constexpr const bool supported = true;
-	static constexpr const size_t size = sizeof (uint16_t);
-
-	static constexpr uint16_t convert(uint16_t v)
+struct Pack::TypeInfo<uint32_t> : public Pack::Convertible {
+	static constexpr void convert(uint32_t &v)
 	{
-		return (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L));
-	}
-};
-
-template <>
-struct Pack::TypeInfo<uint32_t> {
-	static constexpr const bool supported = true;
-	static constexpr const size_t size = sizeof (uint32_t);
-
-	static constexpr uint32_t convert(uint32_t v)
-	{
-		return ((((v) >> 24) & 0x000000FFL)
+		v = ((((v) >> 24) & 0x000000FFL)
 		    | (((v) >> 8)  & 0x0000FF00L)
 		    | (((v) << 8)  & 0x00FF0000L)
 		    | (((v) << 24) & 0xFF000000L));
@@ -207,13 +392,10 @@
 };
 
 template <>
-struct Pack::TypeInfo<uint64_t> {
-	static constexpr const bool supported = true;
-	static constexpr const size_t size = sizeof (uint64_t);
-
-	static constexpr uint64_t convert(uint64_t v)
+struct Pack::TypeInfo<uint64_t> : public Pack::Convertible {
+	static constexpr void convert(uint64_t &v)
 	{
-		return ((((v) & 0xff00000000000000ull) >> 56)
+		v = ((((v) & 0xff00000000000000ull) >> 56)
 			| (((v) & 0x00ff000000000000ull) >> 40)
 			| (((v) & 0x0000ff0000000000ull) >> 24)
 			| (((v) & 0x000000ff00000000ull) >> 8 )
--- a/C++/Tests/Pack/main.cpp	Thu Nov 13 13:23:53 2014 +0100
+++ b/C++/Tests/Pack/main.cpp	Thu Nov 13 13:55:36 2014 +0100
@@ -23,6 +23,31 @@
 
 #include <Pack.h>
 
+struct Point
+{
+	uint32_t width{};
+	uint32_t height{};
+
+	inline bool operator==(const Point &other) const
+	{
+		return width == other.width && height == other.height;
+	}
+};
+
+template <>
+struct Pack::TypeInfo<Point> : public Pack::Serializable
+{
+	static void serialize(PackWriter &writer, const Point &point)
+	{
+		writer << point.width << point.height;
+	}
+
+	static void unserialize(PackReader &reader, Point &point)
+	{
+		reader >> point.width >> point.height;
+	}
+};
+
 TEST(File, simpleLittleEndian)
 {
 	uint8_t u8(1), r8;
@@ -30,16 +55,16 @@
 	uint32_t u32(3), r32;
 	uint64_t u64(4), r64;
 
+	remove("output.bin");
+
 	try {
-		std::ofstream out;
-		out.open("simple-little.bin", std::ostream::out);
-		Pack::write(out, Pack::Little, u8, u16, u32, u64);
-		out.close();
+		{
+			PackFileWriter writer{"output.bin", Pack::Little};
+			writer << u8 << u16 << u32 << u64;
+		}
 
-		std::ifstream in;
-		in.open("simple-little.bin", std::ostream::in);
-		Pack::read(in, Pack::Little, r8, r16, r32, r64);
-		in.close();
+		PackFileReader reader{"output.bin", Pack::Little};
+		reader >> r8 >> r16 >> r32 >> r64;
 
 		ASSERT_EQ(u8, r8);
 		ASSERT_EQ(u16, r16);
@@ -57,16 +82,84 @@
 	uint32_t u32(3), r32;
 	uint64_t u64(4), r64;
 
+	remove("output.bin");
+
 	try {
-		std::ofstream out;
-		out.open("simple-big.bin", std::ostream::out);
-		Pack::write(out, Pack::Big, u8, u16, u32, u64);
-		out.close();
+		{
+			PackFileWriter writer{"output.bin", Pack::Big};
+			writer << u8 << u16 << u32 << u64;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Big};
+		reader >> r8 >> r16 >> r32 >> r64;
+
+		ASSERT_EQ(u8, r8);
+		ASSERT_EQ(u16, r16);
+		ASSERT_EQ(u32, r32);
+		ASSERT_EQ(u64, r64);
+	} catch (const std::exception &error) {
+		FAIL() << error.what();
+	}
+}
+
+TEST(File, arrayLittleEndian)
+{
+	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
+	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
+	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
+	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
+
+	std::vector<uint8_t> r8(8);
+	std::vector<uint16_t> r16(8);
+	std::vector<uint32_t> r32(8);
+	std::vector<uint64_t> r64(8);
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Little};
+
+			writer << u8 << u16 << u32 << u64;
+		}
 
-		std::ifstream in;
-		in.open("simple-big.bin", std::ostream::in);
-		Pack::read(in, Pack::Big, r8, r16, r32, r64);
-		in.close();
+		PackFileReader reader{"output.bin", Pack::Little};
+
+		reader >> r8 >> r16 >> r32 >> r64;
+
+		ASSERT_EQ(u8, r8);
+		ASSERT_EQ(u16, r16);
+		ASSERT_EQ(u32, r32);
+		ASSERT_EQ(u64, r64);
+	} catch (const std::exception &error) {
+		FAIL() << error.what();
+	}
+}
+
+TEST(File, arrayBigEndian)
+{
+	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
+	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
+	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
+	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
+
+	std::vector<uint8_t> r8(8);
+	std::vector<uint16_t> r16(8);
+	std::vector<uint32_t> r32(8);
+	std::vector<uint64_t> r64(8);
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Big};
+
+			writer << u8 << u16 << u32 << u64;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Big};
+
+		reader >> r8 >> r16 >> r32 >> r64;
 
 		ASSERT_EQ(u8, r8);
 		ASSERT_EQ(u16, r16);
@@ -77,6 +170,102 @@
 	}
 }
 
+TEST(File, serializeSimpleLittleEndian)
+{
+	Point point{200, 400};
+	Point result;
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Little};
+
+			writer << point;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Little};
+
+		reader >> result;
+
+		ASSERT_EQ(point, result);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+TEST(File, serializeSimpleBigEndian)
+{
+	Point point{200, 400};
+	Point result;
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Big};
+
+			writer << point;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Big};
+
+		reader >> result;
+
+		ASSERT_EQ(point, result);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+TEST(File, serializeArrayLittleEndian)
+{
+	std::vector<Point> points{{10, 20}, {30, 40}};
+	std::vector<Point> rpoints(2);
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Little};
+
+			writer << points;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Little};
+
+		reader >> rpoints;
+
+		ASSERT_EQ(points, rpoints);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+TEST(File, serializeArrayBigEndian)
+{
+	std::vector<Point> points{{10, 20}, {30, 40}};
+	std::vector<Point> rpoints(2);
+
+	remove("output.bin");
+
+	try {
+		{
+			PackFileWriter writer{"output.bin", Pack::Big};
+
+			writer << points;
+		}
+
+		PackFileReader reader{"output.bin", Pack::Big};
+
+		reader >> rpoints;
+
+		ASSERT_EQ(points, rpoints);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
 TEST(String, simpleLittleEndian)
 {
 	uint8_t u8(1), r8;
@@ -85,11 +274,16 @@
 	uint64_t u64(4), r64;
 
 	try {
-		std::ostringstream out;
-		Pack::write(out, Pack::Little, u8, u16, u32, u64);
+		std::string input;
 
-		std::istringstream in(out.str());
-		Pack::read(in, Pack::Little, r8, r16, r32, r64);
+		{
+			PackStringWriter writer{Pack::Little};
+			writer << u8 << u16 << u32 << u64;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Little};
+		reader >> r8 >> r16 >> r32 >> r64;
 
 		ASSERT_EQ(u8, r8);
 		ASSERT_EQ(u16, r16);
@@ -108,11 +302,86 @@
 	uint64_t u64(4), r64;
 
 	try {
-		std::ostringstream out;
-		Pack::write(out, Pack::Big, u8, u16, u32, u64);
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Big};
+			writer << u8 << u16 << u32 << u64;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Big};
+		reader >> r8 >> r16 >> r32 >> r64;
+
+		ASSERT_EQ(u8, r8);
+		ASSERT_EQ(u16, r16);
+		ASSERT_EQ(u32, r32);
+		ASSERT_EQ(u64, r64);
+	} catch (const std::exception &error) {
+		FAIL() << error.what();
+	}
+}
+
+TEST(String, arrayLittleEndian)
+{
+	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
+	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
+	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
+	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
+
+	std::vector<uint8_t> r8(8);
+	std::vector<uint16_t> r16(8);
+	std::vector<uint32_t> r32(8);
+	std::vector<uint64_t> r64(8);
+
+	try {
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Little};
 
-		std::istringstream in(out.str());
-		Pack::read(in, Pack::Big, r8, r16, r32, r64);
+			writer << u8 << u16 << u32 << u64;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Little};
+
+		reader >> r8 >> r16 >> r32 >> r64;
+
+		ASSERT_EQ(u8, r8);
+		ASSERT_EQ(u16, r16);
+		ASSERT_EQ(u32, r32);
+		ASSERT_EQ(u64, r64);
+	} catch (const std::exception &error) {
+		FAIL() << error.what();
+	}
+}
+
+TEST(String, arrayBigEndian)
+{
+	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
+	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
+	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
+	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
+
+	std::vector<uint8_t> r8(8);
+	std::vector<uint16_t> r16(8);
+	std::vector<uint32_t> r32(8);
+	std::vector<uint64_t> r64(8);
+
+	try {
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Big};
+
+			writer << u8 << u16 << u32 << u64;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Big};
+
+		reader >> r8 >> r16 >> r32 >> r64;
 
 		ASSERT_EQ(u8, r8);
 		ASSERT_EQ(u16, r16);
@@ -123,73 +392,103 @@
 	}
 }
 
-TEST(Array, simpleLittleEndian)
+TEST(String, serializeSimpleLittleEndian)
 {
-	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
-	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
-	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
-	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
-
-	std::vector<uint8_t> r8;
-	std::vector<uint16_t> r16;
-	std::vector<uint32_t> r32;
-	std::vector<uint64_t> r64;
+	Point point{200, 400};
+	Point result;
 
 	try {
-		std::ostringstream out;
-		Pack::writeArray(out, Pack::Little, u8.cbegin(), u8.size());
-		Pack::writeArray(out, Pack::Little, u16.cbegin(), u16.size());
-		Pack::writeArray(out, Pack::Little, u32.cbegin(), u32.size());
-		Pack::writeArray(out, Pack::Little, u64.cbegin(), u64.size());
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Little};
 
-		std::istringstream in(out.str());
-		Pack::readArray(in, Pack::Little, std::back_inserter(r8), 8);
-		Pack::readArray(in, Pack::Little, std::back_inserter(r16), 8);
-		Pack::readArray(in, Pack::Little, std::back_inserter(r32), 8);
-		Pack::readArray(in, Pack::Little, std::back_inserter(r64), 8);
+			writer << point;
+			input = writer.buffer();
+		}
 
+		PackStringReader reader{std::move(input), Pack::Little};
 
-		ASSERT_EQ(u8, r8);
-		ASSERT_EQ(u16, r16);
-		ASSERT_EQ(u32, r32);
-		ASSERT_EQ(u64, r64);
-	} catch (const std::exception &error) {
-		FAIL() << error.what();
+		reader >> result;
+
+		ASSERT_EQ(point, result);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
 	}
 }
 
-TEST(Array, simpleBigEndian)
+TEST(String, serializeSimpleBigEndian)
 {
-	std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
-	std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 };
-	std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 };
-	std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 };
+	Point point{200, 400};
+	Point result;
+
+	try {
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Big};
+
+			writer << point;
+			input = writer.buffer();
+		}
 
-	std::vector<uint8_t> r8;
-	std::vector<uint16_t> r16;
-	std::vector<uint32_t> r32;
-	std::vector<uint64_t> r64;
+		PackStringReader reader{std::move(input), Pack::Big};
+
+		reader >> result;
+
+		ASSERT_EQ(point, result);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+TEST(String, serializeArrayLittleEndian)
+{
+	std::vector<Point> points{{10, 20}, {30, 40}};
+	std::vector<Point> rpoints(2);
 
 	try {
-		std::ostringstream out;
-		Pack::writeArray(out, Pack::Big, u8.cbegin(), u8.size());
-		Pack::writeArray(out, Pack::Big, u16.cbegin(), u16.size());
-		Pack::writeArray(out, Pack::Big, u32.cbegin(), u32.size());
-		Pack::writeArray(out, Pack::Big, u64.cbegin(), u64.size());
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Little};
+
+			writer << points;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Little};
+
+		reader >> rpoints;
+
+		ASSERT_EQ(points, rpoints);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
 
-		std::istringstream in(out.str());
-		Pack::readArray(in, Pack::Big, std::back_inserter(r8), 8);
-		Pack::readArray(in, Pack::Big, std::back_inserter(r16), 8);
-		Pack::readArray(in, Pack::Big, std::back_inserter(r32), 8);
-		Pack::readArray(in, Pack::Big, std::back_inserter(r64), 8);
+TEST(String, serializeArrayBigEndian)
+{
+	std::vector<Point> points{{10, 20}, {30, 40}};
+	std::vector<Point> rpoints(2);
 
+	try {
+		std::string input;
+
+		{
+			PackStringWriter writer{Pack::Big};
 
-		ASSERT_EQ(u8, r8);
-		ASSERT_EQ(u16, r16);
-		ASSERT_EQ(u32, r32);
-		ASSERT_EQ(u64, r64);
-	} catch (const std::exception &error) {
-		FAIL() << error.what();
+			writer << points;
+			input = writer.buffer();
+		}
+
+		PackStringReader reader{std::move(input), Pack::Big};
+
+		reader >> rpoints;
+
+		ASSERT_EQ(points, rpoints);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
 	}
 }
 
--- a/C++/TreeNode.h	Thu Nov 13 13:23:53 2014 +0100
+++ b/C++/TreeNode.h	Thu Nov 13 13:55:36 2014 +0100
@@ -52,14 +52,17 @@
 	template <typename U>
 	class HasEqualsTo {
 	public:
+		using Yes	= char [2];
+		using No	= char [1];
+
 		static_assert(sizeof (char) != sizeof (long), "buy a new compiler");
 
 		template <typename Value>
-		static constexpr auto check(Value *u) -> decltype(*u == *u, char(0));
+		static constexpr Yes &check(Value *u, decltype(*u == *u) * = nullptr);
 
-		static constexpr long check(...);
+		static constexpr No &check(...);
 
-		static constexpr const bool value = sizeof (check((U *)0)) == sizeof (char);
+		static constexpr const bool value = sizeof (check((U *)0)) == sizeof (Yes);
 	};
 
 public:
@@ -78,39 +81,53 @@
 template <typename T>
 class TreeNode {
 private:
-	struct Object {
-		std::unique_ptr<T> value;
-
+	class Object {
+	public:
+		virtual T &get() = 0;
 		virtual std::unique_ptr<Object> clone() const = 0;
 	};
 
-	template <typename U>
+	template <typename Value>
 	struct Proxy final : public Object {
-		inline Proxy(U &&u)
+	private:
+		Value m_value;
+
+		Proxy(const Proxy &) = delete;
+		Proxy &operator=(const Proxy &) = delete;
+		Proxy(Proxy &&) = delete;
+		Proxy &operator=(Proxy &&) = delete;
+
+	public:
+		inline Proxy(Value &&value)
+			: m_value(std::move(value))
 		{
-			this->value = std::make_unique<U>(std::move(u));
 		}
 
-		inline Proxy(const U &u)
+		inline Proxy(const Value &value)
+			: m_value(value)
 		{
-			this->value = std::make_unique<U>(u);
 		}
 
 		template <typename... Args>
 		inline Proxy(Args&&... args)
+			: m_value(std::forward<Args>(args)...)
 		{
-			this->value = std::make_unique<U>(std::forward<Args>(args)...);
+		}
+
+		T &get() override
+		{
+			return m_value;
 		}
 
 		std::unique_ptr<Object> clone() const override
 		{
-			return std::make_unique<Proxy<U>>(static_cast<const U &>(*this->value));
+			return std::make_unique<Proxy<Value>>(m_value);
 		}	
 	};
 
 	using Container	= std::deque<std::unique_ptr<Object>>;
 
-	TreeNode	*m_parent { nullptr };
+	TreeNode	*m_parent{nullptr};
 	Container	 m_children;
 
 public:
@@ -134,7 +151,7 @@
 	{
 		for (const auto &c : other.m_children) {
 			m_children.push_back(c->clone());
-			m_children.back()->value->m_parent = this;
+			m_children.back()->get().m_parent = this;
 		}
 	}
 
@@ -149,7 +166,7 @@
 	{
 		// Update children to update to *this
 		for (auto &c : m_children)
-			c->value->m_parent = this;
+			c->get().m_parent = this;
 
 		other.m_children.clear();
 	}
@@ -165,7 +182,7 @@
 
 		for (const auto &c : other.m_children) {
 			m_children.push_back(c->clone());
-			m_children.back()->value->m_parent = this;
+			m_children.back()->get().m_parent = this;
 		}
 
 		return *this;
@@ -182,7 +199,7 @@
 
 		// Update children to update to *this
 		for (auto &c : m_children)
-			c->value->m_parent = this;
+			c->get().m_parent = this;
 
 		other.m_children.clear();
 
@@ -198,7 +215,7 @@
 	inline void push(const Value &child)
 	{
 		m_children.push_front(std::make_unique<Proxy<Value>>(child));
-		m_children.front()->value->m_parent = this;
+		m_children.front()->get().m_parent = this;
 	}
 
 	/**
@@ -212,7 +229,7 @@
 		using Type = typename std::decay<Value>::type;
 
 		m_children.push_front(std::make_unique<Proxy<Type>>(std::move(child)));
-		m_children.front()->value->m_parent = this;
+		m_children.front()->get().m_parent = this;
 	}
 
 	/**
@@ -224,7 +241,7 @@
 	void pushNew(Args&&... args)
 	{
 		m_children.emplace_front(std::make_unique<Proxy<T>>(std::forward<Args>(args)...));
-		m_children.front()->value->m_parent = this;
+		m_children.front()->get().m_parent = this;
 	}
 
 	/**
@@ -236,7 +253,7 @@
 	inline void append(const Value &child)
 	{
 		m_children.push_back(std::make_unique<Proxy<Value>>(child));
-		m_children.back()->value->m_parent = this;
+		m_children.back()->get().m_parent = this;
 	}
 
 	/**
@@ -250,7 +267,7 @@
 		using Type = typename std::decay<Value>::type;
 
 		m_children.push_back(std::make_unique<Proxy<Type>>(std::move(child)));
-		m_children.back()->value->m_parent = this;
+		m_children.back()->get().m_parent = this;
 	}
 
 	/**
@@ -262,7 +279,7 @@
 	void appendNew(Args&&... args)
 	{
 		m_children.emplace_back(std::make_unique<Proxy<T>>(std::forward<Args>(args)...));
-		m_children.back()->value->m_parent = this;
+		m_children.back()->get().m_parent = this;
 	}
 
 	/**
@@ -347,7 +364,7 @@
 	void remove(T &value)
 	{
 		m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) {
-			return p->value.get() == &value;
+			return &p->get() == &value;
 		}), m_children.end());
 	}
 
@@ -361,7 +378,7 @@
 	void removeSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr)
 	{
 		m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) {
-			return *p->value == value;
+			return p->get() == value;
 		}), m_children.end());
 	}
 
@@ -383,7 +400,7 @@
 	int indexOf(const T &child) const noexcept
 	{
 		for (unsigned i = 0; i < m_children.size(); ++i)
-			if (m_children[i]->value.get() == &child)
+			if (&m_children[i]->get() == &child)
 				return i;
 
 		return -1;
@@ -400,7 +417,7 @@
 	int indexOfSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) const noexcept
 	{
 		for (unsigned i = 0; i < m_children.size(); ++i)
-			if (*m_children[i]->value == value)
+			if (m_children[i]->get() == value)
 				return i;
 
 		return -1;
@@ -415,7 +432,7 @@
 	 */
 	T &operator[](int index)
 	{
-		return static_cast<T &>(*m_children.at(index)->value);
+		return static_cast<T &>(m_children.at(index)->get());
 	}
 
 	/**
@@ -427,7 +444,7 @@
 	 */
 	const T &operator[](int index) const
 	{
-		return static_cast<const T &>(*m_children.at(index)->value);
+		return static_cast<const T &>(m_children.at(index)->get());
 	}
 };