view C++/Pack.h @ 263:fb91d4dc046b

Removal of Luae
author David Demelier <markand@malikania.fr>
date Tue, 14 Oct 2014 09:14:33 +0200
parents c6513d9c696b
children 41bdde9027c0
line wrap: on
line source

/*
 * Pack.h -- binary data serialization
 *
 * Copyright (c) 2013 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 <stdexcept>
#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 {
public:
	/**
	 * @enum Endian
	 * @brief Endian mode
	 */
	enum Endian {
		Little,		//! Little endian
		Big		//! Big endian
	};

private:
	static void writeFile(std::ofstream &, Endian)
	{
		// dummy, stop recursion
	}

	template <typename T, typename... Args>
	static void writeFile(std::ofstream &out, Endian endian, const T &value, Args&... args)
	{
		static_assert(TypeInfo<T>::supported, "unsupported type");

		T ret = convert(value, endian);
		out.write(reinterpret_cast<const char *>(&ret), TypeInfo<T>::size);
		writeFile(out, endian, args...);
	}

	static void readFile(std::ifstream &, Endian)
	{
		// dummy, stop recursion
	}

	template <typename T, typename... Args>
	static void readFile(std::ifstream &in, Endian endian, T &value, Args&&... args)
	{
		static_assert(TypeInfo<T>::supported, "unsupported type");

		in.read(reinterpret_cast<char *>(&value), TypeInfo<T>::size);
		value = convert(value, endian);
		readFile(in, endian, args...);
	}

public:
	/**
	 * Host system endian mode.
	 */
	static const Endian mode;

	/**
	 * @struct TypeInfo
	 * @brief Type information
	 *
	 * Used for conversions.
	 */
	template <typename T>
	struct TypeInfo {
		static const bool supported = false;
		static const size_t size = 0;
	};

	/**
	 * Convert data.
	 *
	 * @param value the value to convert if needed
	 * @param endian the endian mode
	 * @return the converted value
	 */
	template <typename T>
	static T convert(T value, Endian endian);

	/**
	 * Write binary data to the file
	 *
	 * @param path the path to the file
	 * @param endian the endian mode
	 * @param args the arguments
	 * @throw std::runtime_exception on error
	 */
	template <typename... Args>
	static void write(const std::string &path,
			  Endian endian,
			  Args&&... args)
	{
		std::ofstream out;

		out.open(path, std::ios_base::binary);
		if (!out.is_open())
			throw std::runtime_error("Can't open file for writing");

		writeFile(out, endian, std::forward<Args>(args)...);
	}

	/**
	 * Read binary data from the file
	 *
	 * @param path the path to the file
	 * @param endian the endian mode
	 * @param args the arguments
	 * @throw std::runtime_exception on error
	 */
	template <typename... Args>
	static void read(const std::string &path,
			 Endian endian,
			 Args&&... args)
	{
		std::ifstream in;

		in.open(path, std::ios_base::binary);
		if (!in.is_open())
			throw std::runtime_error("Can't open file for reading");

		readFile(in, endian, std::forward<Args>(args)...);
	}
};

template <>
struct Pack::TypeInfo<uint8_t> {
	static const bool supported = true;
	static const size_t size = 1;
};

template <>
struct Pack::TypeInfo<uint16_t> {
	static const bool supported = true;
	static const size_t size = 2;
};

template <>
struct Pack::TypeInfo<uint32_t> {
	static const bool supported = true;
	static const size_t size = 4;
};

template <>
struct Pack::TypeInfo<uint64_t> {
	static const bool supported = true;
	static const size_t size = 8;
};

template <>
inline uint8_t Pack::convert(uint8_t v, Endian)
{
	return v;
}

template <>
inline uint16_t Pack::convert(uint16_t v, Endian endian)
{
	if (mode != endian)
		return (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L));

	return v;
}

template <>
inline uint32_t Pack::convert(uint32_t v, Endian endian)
{
	if (mode != endian)
		return ((((v) >> 24) & 0x000000FFL)
		    | (((v) >> 8)  & 0x0000FF00L)
		    | (((v) << 8)  & 0x00FF0000L)
		    | (((v) << 24) & 0xFF000000L));

	return v;
}

template <>
inline uint64_t Pack::convert(uint64_t v, Endian endian)
{
	if (mode != endian)
		return ((((v) & 0xff00000000000000ull) >> 56)
			| (((v) & 0x00ff000000000000ull) >> 40)
			| (((v) & 0x0000ff0000000000ull) >> 24)
			| (((v) & 0x000000ff00000000ull) >> 8 )
			| (((v) & 0x00000000ff000000ull) << 8 )
			| (((v) & 0x0000000000ff0000ull) << 24)
			| (((v) & 0x000000000000ff00ull) << 40)
			| (((v) & 0x00000000000000ffull) << 56));

	return v;
}

#endif // !_PACK_H_