Добавлен файл с типами для прозрачного преобразования порядка байтов

This commit is contained in:
Andrei Astafev 2020-05-14 10:43:38 +03:00
parent f845b2fda1
commit 09d1e12a44
3 changed files with 218 additions and 1 deletions

@ -1 +1 @@
Subproject commit 959608f6b5d8c6e4add9f9e7422109bc6b795a5f Subproject commit 7324d80f3dc97ee9a1da464e9ed7389e00fff880

View File

@ -10,6 +10,7 @@ set(TRGT_cpp
set(TRGT_hpp set(TRGT_hpp
${CMAKE_CURRENT_SOURCE_DIR}/config.hpp ${CMAKE_CURRENT_SOURCE_DIR}/config.hpp
${CMAKE_CURRENT_SOURCE_DIR}/limits.hpp ${CMAKE_CURRENT_SOURCE_DIR}/limits.hpp
${CMAKE_CURRENT_SOURCE_DIR}/endian_types.hpp
${CMAKE_CURRENT_SOURCE_DIR}/enum_bitmask_operations.hpp) ${CMAKE_CURRENT_SOURCE_DIR}/enum_bitmask_operations.hpp)
set(TRGT_headers ${TRGT_hpp}) set(TRGT_headers ${TRGT_hpp})

View File

@ -0,0 +1,216 @@
#ifndef MYX_BASE_ENDIAN_TYPES_HPP_
#define MYX_BASE_ENDIAN_TYPES_HPP_
#pragma once
#include <cassert>
#include <cstdint>
#include <iostream>
#ifdef QT_CORE_LIB
#include <QDebug>
#endif
// Both LittleEndianType and BigEndianType are manufactured primitives
// that are essentially immutable except for the assignment operators.
// All math logic happens outside of the class in the native format
// and an internal set/swap method is used to store the data.
namespace myx {
namespace base {
template< typename T >
class EndianTypesBase
{
protected:
#if _MSC_VER
#pragma warning ( push )
#endif
static T swapBytes( const T& b )
{
T n;
switch ( sizeof( T ) )
{
case 8: // 64-bit
( reinterpret_cast< uint8_t* >( &n ) )[0] = ( reinterpret_cast< const uint8_t* >( &b ) )[7];
( reinterpret_cast< uint8_t* >( &n ) )[1] = ( reinterpret_cast< const uint8_t* >( &b ) )[6];
( reinterpret_cast< uint8_t* >( &n ) )[2] = ( reinterpret_cast< const uint8_t* >( &b ) )[5];
( reinterpret_cast< uint8_t* >( &n ) )[3] = ( reinterpret_cast< const uint8_t* >( &b ) )[4];
( reinterpret_cast< uint8_t* >( &n ) )[4] = ( reinterpret_cast< const uint8_t* >( &b ) )[3];
( reinterpret_cast< uint8_t* >( &n ) )[5] = ( reinterpret_cast< const uint8_t* >( &b ) )[2];
( reinterpret_cast< uint8_t* >( &n ) )[6] = ( reinterpret_cast< const uint8_t* >( &b ) )[1];
( reinterpret_cast< uint8_t* >( &n ) )[7] = ( reinterpret_cast< const uint8_t* >( &b ) )[0];
break;
case 4: // 32-bit
( reinterpret_cast< uint8_t* >( &n ) )[0] = ( reinterpret_cast< const uint8_t* >( &b ) )[3];
( reinterpret_cast< uint8_t* >( &n ) )[1] = ( reinterpret_cast< const uint8_t* >( &b ) )[2];
( reinterpret_cast< uint8_t* >( &n ) )[2] = ( reinterpret_cast< const uint8_t* >( &b ) )[1];
( reinterpret_cast< uint8_t* >( &n ) )[3] = ( reinterpret_cast< const uint8_t* >( &b ) )[0];
break;
case 2: // 16-bit
( reinterpret_cast< uint8_t* >( &n ) )[0] = ( reinterpret_cast< const uint8_t* >( &b ) )[1];
( reinterpret_cast< uint8_t* >( &n ) )[1] = ( reinterpret_cast< const uint8_t* >( &b ) )[0];
break;
default:
assert( 0 ); // Endian swap is only defined for 2, 4, and 8-byte types
break;
} // switch
return( n );
} // swapBytes
#if _MSC_VER
#pragma warning ( pop )
#endif
}; // class EndianTypesBase
template< typename T >
class LittleEndianType : public EndianTypesBase< T >
{
protected:
T m_data;
static T transform( const T& b )
{
const uint16_t i = 1;
return( ( reinterpret_cast< const char& >( i ) == 1 ) ? b : EndianTypesBase< T >::swapBytes( b ) );
}
public:
// Constructors
LittleEndianType() = default;
// If we endian swap, it happens in two places:
// 1. Set an OE from a PE
// 2. Get a PE from an OE
// Storage in
explicit LittleEndianType( const T& b ) :
m_data( transform( b ) )
{}
template < typename U > explicit LittleEndianType( U const& b ) :
m_data( transform( T( b ) ) )
{}
// Storage out
template < typename U > explicit operator U() const { return( U( transform( m_data ) ) ); }
explicit operator T() const { return( transform( m_data ) ); }
template < typename U > bool operator ==( U const& o ) { return( U( *this ) == o ); }
template < typename U > bool operator !=( U const& o ) { return( U( *this ) != o ); }
// Arithmetic assignment operators
LittleEndianType& operator ++() /* prefix */ { *this = T( *this ) + T( 1 ); return( *this ); }
LittleEndianType operator ++( int ) /* suffix */ { LittleEndianType t( *this ); *this = T( *this ) + T( 1 ); return( t ); }
LittleEndianType& operator --() /* prefix */ { *this = T( *this ) - T( 1 ); return( *this ); }
LittleEndianType operator --( int ) /* suffix */ { LittleEndianType t( *this ); *this = T( *this ) - T( 1 ); return( t ); }
// Compound assignment operators
LittleEndianType& operator +=( const T& b ) { *this = T( *this ) + b; return( *this ); }
LittleEndianType& operator -=( const T& b ) { *this = T( *this ) - b; return( *this ); }
LittleEndianType& operator *=( const T& b ) { *this = T( *this ) * b; return( *this ); }
LittleEndianType& operator /=( const T& b ) { *this = T( *this ) / b; return( *this ); }
LittleEndianType& operator %=( const T& b ) { *this = T( *this ) % b; return( *this ); }
LittleEndianType& operator &=( const T& b ) { *this = T( *this ) & b; return( *this ); }
LittleEndianType& operator |=( const T& b ) { *this = T( *this ) | b; return( *this ); }
LittleEndianType& operator ^=( const T& b ) { *this = T( *this ) ^ b; return( *this ); }
LittleEndianType& operator <<=( const T& b ) { *this = T( T( *this ) << b ); return( *this ); }
LittleEndianType& operator >>=( const T& b ) { *this = T( T( *this ) >> b ); return( *this ); }
friend std::ostream& operator <<( std::ostream& out, const LittleEndianType b ) { out << T( b ); return( out ); }
friend std::istream& operator >>( std::istream& in, LittleEndianType& b ) { T val; in >> val; b = val; return( in ); }
std::string toStdString() { return( std::to_string( transform( m_data ) ) ); }
#ifdef QT_CORE_LIB
friend QDebug& operator <<( QDebug& out, const BigEndianType b ) { out << T( b ); return( out ); }
QString toQString() { return( QString::number( transform( m_data ) ) ); }
#endif
}; // class LittleEndianType
template< typename T >
class BigEndianType : public EndianTypesBase< T >
{
protected:
T m_data;
static T transform( const T& b )
{
const uint16_t i = 1;
return( ( reinterpret_cast< const char& >( i ) == 1 ) ? EndianTypesBase< T >::swapBytes( b ) : b );
}
public:
// Constructors
BigEndianType() = default;
// If we endian swap, it happens in two places:
// 1. Set an OE from a PE
// 2. Get a PE from an OE
// Storage in
explicit BigEndianType( const T& b ) :
m_data( transform( b ) )
{}
template < typename U > explicit BigEndianType( U const& b ) :
m_data( transform( T( b ) ) )
{}
// Storage out
template < typename U > explicit operator U() const { return( U( transform( m_data ) ) ); }
explicit operator T() const { return( transform( m_data ) ); }
template < typename U > bool operator ==( U const& o ) { return( U( *this ) == o ); }
template < typename U > bool operator !=( U const& o ) { return( U( *this ) != o ); }
// Arithmetic assignment operators
BigEndianType& operator ++() /* prefix */ { *this = T( *this ) + 1; return( *this ); }
BigEndianType operator ++( int ) /* suffix */ { BigEndianType t( *this ); *this = T( *this ) + 1; return( t ); }
BigEndianType& operator --() /* prefix */ { *this = T( *this ) - 1; return( *this ); }
BigEndianType operator --( int ) /* suffix */ { BigEndianType t( *this ); *this = T( *this ) - 1; return( t ); }
// Compound assignment operators
BigEndianType& operator +=( const T& b ) { *this = T( *this ) + b; return( *this ); }
BigEndianType& operator -=( const T& b ) { *this = T( *this ) - b; return( *this ); }
BigEndianType& operator *=( const T& b ) { *this = T( *this ) * b; return( *this ); }
BigEndianType& operator /=( const T& b ) { *this = T( *this ) / b; return( *this ); }
BigEndianType& operator %=( const T& b ) { *this = T( *this ) % b; return( *this ); }
BigEndianType& operator &=( const T& b ) { *this = T( *this ) & b; return( *this ); }
BigEndianType& operator |=( const T& b ) { *this = T( *this ) | b; return( *this ); }
BigEndianType& operator ^=( const T& b ) { *this = T( *this ) ^ b; return( *this ); }
BigEndianType& operator <<=( const T& b ) { *this = T( T( *this ) << b ); return( *this ); }
BigEndianType& operator >>=( const T& b ) { *this = T( T( *this ) >> b ); return( *this ); }
friend std::ostream& operator <<( std::ostream& out, const BigEndianType b ) { out << T( b ); return( out ); }
friend std::istream& operator >>( std::istream& in, BigEndianType& b ) { T val; in >> val; b = val; return( in ); }
std::string toStdString() { return( std::to_string( transform( m_data ) ) ); }
#ifdef QT_CORE_LIB
friend QDebug& operator <<( QDebug& out, const BigEndianType b ) { out << T( b ); return( out ); }
QString toQString() { return( QString::number( transform( m_data ) ) ); }
#endif
}; // class BigEndianType
} // namespace base
} // namespace myx
using leint16 = myx::base::LittleEndianType< int16_t >;
using leint32 = myx::base::LittleEndianType< int32_t >;
using leint64 = myx::base::LittleEndianType< int64_t >;
using leuint16 = myx::base::LittleEndianType< uint16_t >;
using leuint32 = myx::base::LittleEndianType< uint32_t >;
using leuint64 = myx::base::LittleEndianType< uint64_t >;
using lefloat = myx::base::LittleEndianType< float >;
using ledouble = myx::base::LittleEndianType< double >;
using beint16 = myx::base::BigEndianType< int16_t >;
using beint32 = myx::base::BigEndianType< int32_t >;
using beint64 = myx::base::BigEndianType< int64_t >;
using beuint16 = myx::base::BigEndianType< uint16_t >;
using beuint32 = myx::base::BigEndianType< uint32_t >;
using beuint64 = myx::base::BigEndianType< uint64_t >;
using befloat = myx::base::BigEndianType< float >;
using bedouble = myx::base::BigEndianType< double >;
#endif // MYX_BASE_ENDIAN_TYPES_HPP_