Mercurial > code
view C++/modules/Pack/Pack.h @ 334:0b576ee64d45
* Create brand new hierarchy
* Rename DynLib to Dynlib
* Remove some warnings
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 08 Mar 2015 14:26:33 +0100 |
parents | C++/Pack.h@2935e07ddb88 |
children |
line wrap: on
line source
/* * Pack.h -- binary data serialization * * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _PACK_H_ #define _PACK_H_ #include <cstdint> #include <fstream> #include <memory> #include <sstream> #include <string> /** * @class Pack * @brief Serialize binary data to files * * This class write and read binary data from files. It currently * support: * uint8_t, * uint16_t, * uint32_t, * uint64_t */ class Pack { private: template <typename T> struct IsContainer { using Yes = char [1]; using No = char [2]; template <typename U> static constexpr Yes &test(typename U::value_type *); template <typename U> static constexpr No &test(...); static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes); }; friend class PackWriter; friend class PackReader; public: /** * @enum Endian * @brief Endian mode */ enum Endian { Little, //! Little endian Big //! Big endian }; public: /** * Host system endian mode. */ static const Endian mode; /** * @struct TypeInfo * @brief Type information * * Used for conversions. */ template <typename T> struct TypeInfo { 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 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 inplace. * * @param value the value */ template <typename T> static inline void convert(T &value) noexcept { 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; public: /** * Default destructor. */ virtual ~PackReader() = default; /** * 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; } /** * Read a serializable type. * * @param value the value destination * @return *this */ template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> PackReader &operator>>(T &value) { Pack::TypeInfo<T>::unserialize(*this, value); return *this; } /** * 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. * * 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 std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> PackReader &operator>>(T &container) { 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; } /** * Write a container to the stream. * * @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 */ 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; 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); /** * The current buffer. Returns a copy of the string. * * @return the string */ std::string buffer() const; }; template <> struct Pack::TypeInfo<uint8_t> : public Pack::Convertible { static inline void convert(uint8_t &) noexcept { // uint8_t are endian independent } }; template <> struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { static inline void convert(uint16_t &v) { v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); } }; template <> struct Pack::TypeInfo<uint32_t> : public Pack::Convertible { static inline void convert(uint32_t &v) { v = ((((v) >> 24) & 0x000000FFL) | (((v) >> 8) & 0x0000FF00L) | (((v) << 8) & 0x00FF0000L) | (((v) << 24) & 0xFF000000L)); } }; template <> struct Pack::TypeInfo<uint64_t> : public Pack::Convertible { static inline void convert(uint64_t &v) { v = ((((v) & 0xff00000000000000ull) >> 56) | (((v) & 0x00ff000000000000ull) >> 40) | (((v) & 0x0000ff0000000000ull) >> 24) | (((v) & 0x000000ff00000000ull) >> 8 ) | (((v) & 0x00000000ff000000ull) << 8 ) | (((v) & 0x0000000000ff0000ull) << 24) | (((v) & 0x000000000000ff00ull) << 40) | (((v) & 0x00000000000000ffull) << 56)); } }; #endif // !_PACK_H_