You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

284 lines
7.5 KiB

/*
* Copyright (C) 2011-2017 Daniel Scharrer
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the author(s) be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*!
* \file
*
* Utility functions for dealing with different endiannesses.
*/
#ifndef INNOEXTRACT_UTIL_ENDIAN_HPP
#define INNOEXTRACT_UTIL_ENDIAN_HPP
#include <cstdlib>
#include <cstring>
#include "configure.hpp"
#if INNOEXTRACT_HAVE_BSWAP_16 || INNOEXTRACT_HAVE_BSWAP_32 || INNOEXTRACT_HAVE_BSWAP_64
#include <byteswap.h>
#endif
#include <boost/cstdint.hpp>
namespace util {
namespace detail {
inline boost::uint8_t byteswap(boost::uint8_t value) {
return value;
}
inline boost::int8_t byteswap(boost::int8_t value) {
return boost::int8_t(byteswap(boost::uint8_t(value)));
}
inline boost::uint16_t byteswap(boost::uint16_t value) {
#if INNOEXTRACT_HAVE_BUILTIN_BSWAP16
return __builtin_bswap16(value);
#elif defined(_MSC_VER) && _MSC_VER >= 1300
return _byteswap_ushort(value);
#elif INNOEXTRACT_HAVE_BSWAP_16 \
&& (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) \
|| (__GLIBC__ << 16) + __GLIBC_MINOR__ >= (2 << 16) + 16) \
// prevent conversion warnings
return bswap_16(value);
#else
return boost::uint16_t((boost::uint16_t(boost::uint8_t(value)) << 8)
| boost::uint8_t(value >> 8));
#endif
}
inline boost::int16_t byteswap(boost::int16_t value) {
return boost::int16_t(byteswap(boost::uint16_t(value)));
}
inline boost::uint32_t byteswap(boost::uint32_t value) {
#if INNOEXTRACT_HAVE_BUILTIN_BSWAP32
return __builtin_bswap32(value);
#elif defined(_MSC_VER) && (_MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)))
return _byteswap_ulong(value);
#elif INNOEXTRACT_HAVE_BSWAP_32
return bswap_32(value);
#else
return (boost::uint32_t(byteswap(boost::uint16_t(value))) << 16)
| byteswap(boost::uint16_t(value >> 16));
#endif
}
inline boost::int32_t byteswap(boost::int32_t value) {
return boost::int32_t(byteswap(boost::uint32_t(value)));
}
inline boost::uint64_t byteswap(boost::uint64_t value) {
#if INNOEXTRACT_HAVE_BUILTIN_BSWAP64
return __builtin_bswap64(value);
#elif defined(_MSC_VER) && _MSC_VER >= 1300
return _byteswap_uint64(value);
#elif INNOEXTRACT_HAVE_BSWAP_64
return bswap_64(value);
#else
return (boost::uint64_t(byteswap(boost::uint32_t(value))) << 32)
| byteswap(boost::uint32_t(value >> 32));
#endif
}
inline boost::int64_t byteswap(boost::int64_t value) {
return boost::int64_t(byteswap(boost::uint64_t(value)));
}
} // namespace detail
//! Load/store functions for a specific endianness.
template <typename Endianness>
struct endianness {
/*!
* Load a single integer.
*
* \param buffer Memory location containing the integer. Will read sizeof(T) bytes.
* \return the loaded integer.
*/
template <typename T>
static T load(const char * buffer) {
if(Endianness::native()) {
T value;
std::memcpy(&value, buffer, sizeof(value));
return value;
} else {
return load_alien<T>(buffer);
}
}
/*!
* Load an array of integers.
*
* \param buffer Memory location containing the integers (without padding).
* Will read <code>sizeof(T) * count</code> bytes.
* \param values Output array for the loaded integers.
* \param count How many integers to load.
*/
template <typename T>
static void load(const char * buffer, T * values, size_t count) {
if(Endianness::native() || sizeof(*values) == 1) {
std::memcpy(values, buffer, sizeof(*values) * count);
} else {
for(size_t i = 0; i < count; i++, buffer += sizeof(*values)) {
values[i] = load_alien<T>(buffer);
}
}
}
/*!
* Store a single integer.
*
* \param value The integer to store.
* \param buffer Memory location to receive the integer. Will write sizeof(T) bytes.
*/
template <typename T>
static void store(T value, char * buffer) {
if(Endianness::native()) {
std::memcpy(buffer, &value, sizeof(value));
} else {
return store_alien(value, buffer);
}
}
/*!
* Store an array of integers.
*
* \param values The integers to store.
* \param count How many integers to store.
* \param buffer Memory location to receive the integers (without padding).
* Will write <code>sizeof(T) * count</code> bytes.
*/
template <typename T>
static void store(T * values, size_t count, char * buffer) {
if(Endianness::native() || sizeof(*values) == 1) {
std::memcpy(buffer, values, sizeof(*values) * count);
} else {
for(size_t i = 0; i < count; i++, buffer += sizeof(*values)) {
store_alien(values[i], buffer);
}
}
}
private:
bool reversed() { return false; }
template <typename T>
static T load_alien(const char * buffer) {
if(Endianness::reversed()) {
T value;
std::memcpy(&value, buffer, sizeof(value));
return detail::byteswap(value);
} else {
return Endianness::template decode<T>(buffer);
}
}
template <typename T>
static void store_alien(T value, char * buffer) {
if(Endianness::reversed()) {
value = detail::byteswap(value);
std::memcpy(buffer, &value, sizeof(value));
} else {
Endianness::template encode<T>(value, buffer);
}
}
};
namespace detail {
inline bool is_little_endian() {
boost::uint32_t signature = 0x04030201;
return (*reinterpret_cast<char *>(&signature) == 1);
}
inline bool is_big_endian() {
boost::uint32_t signature = 0x04030201;
return (*reinterpret_cast<char *>(&signature) == 4);
}
} // namespace detail
//! Load and store little-endian integers.
struct little_endian : endianness<little_endian> {
//! \return true if we are running on a little-endian machine.
static bool native() { return detail::is_little_endian(); }
private:
static bool reversed() { return detail::is_big_endian(); }
template <typename T>
static T decode(const char * buffer) {
T value = 0;
for(size_t i = 0; i < sizeof(T); i++) {
value = T(value | (T(buffer[i]) << (i * 8)));
}
return value;
}
template <typename T>
static void encode(T value, char * buffer) {
for(size_t i = 0; i < sizeof(T); i++) {
buffer[i] = char((value >> (i * 8)) & 0xff);
}
}
friend struct endianness<little_endian>;
};
//! Load and store big-endian integers.
struct big_endian : endianness<big_endian> {
//! \return true if we are running on a big-endian machine.
static bool native() { return detail::is_big_endian(); }
private:
static bool reversed() { return detail::is_little_endian(); }
template <typename T>
static T decode(const char * buffer) {
T value = 0;
for(size_t i = 0; i < sizeof(T); i++) {
value = T(value | T(buffer[i]) << ((sizeof(T) - i - 1) * 8));
}
return value;
}
template <typename T>
static void encode(T value, char * buffer) {
for(size_t i = 0; i < sizeof(T); i++) {
buffer[i] = char((value >> ((sizeof(T) - i - 1) * 8)) & 0xff);
}
}
friend struct endianness<big_endian>;
};
} // namespace util
#endif // INNOEXTRACT_UTIL_ENDIAN_HPP