Skip to content

Instantly share code, notes, and snippets.

@DanielG
Created March 27, 2010 13:27
Show Gist options
  • Save DanielG/346045 to your computer and use it in GitHub Desktop.
Save DanielG/346045 to your computer and use it in GitHub Desktop.
Print any type as binary with automatic endian swap
#include "PrintBinary.h"
int main ()
{
printBinary(float_sign_mask, kPB_GROUP);
}
#include <iostream>
#include <vector>
#include <stdarg.h>
#include <stdint.h>
enum printBinary_opt
{
kPB_NO_OPT = 0,
kPB_LITTLE_ENDIAN = 1,
kPB_BIG_ENDIAN = (1 << 1),
kPB_GROUP = (1 << 2),
};
void printBinaryByte(uint8_t byte);
template <typename T>
void printBinary(T var, int flags = kPB_NO_OPT);
/// \brief Storage for a byte swap sequence, providing swapping methodes
/// The ByteSwapSequence class provides storage for the sequence to swap
/// the bytes of variables passed to it's methodes in.
template <typename T>
class ByteSwapSequence
{
public:
typedef std::vector<size_t> BOrder;
ByteSwapSequence() { for(uint8_t i=0; i < sizeof(T) ;++i) { swappingSequences.push_back(i); } }
ByteSwapSequence(BOrder swappingSeqs) { swappingSequences = swappingSeqs; }
ByteSwapSequence(uint8_t size, ...)
{
va_list argptr;
va_start( argptr, size );
for(uint8_t i=0; i < size ;++i)
{
swappingSequences.push_back(va_arg( argptr, uint8_t ));
}
va_end( argptr );
}
T networkToHost(const T networkVar)
{
assert(swappingSequences.size() == sizeof(T));
const uint8_t* cNet = reinterpret_cast<const uint8_t*> (&networkVar);
uint8_t* swapped = new uint8_t[sizeof(T)];
for(uint8_t i=0; i < sizeof(T) ;++i)
{
swapped[swappingSequences[i]] = cNet[i];
}
return *reinterpret_cast<T*> (swapped);
}
T hostToNetwork(const T hostVar)
{
assert(swappingSequences.size() == sizeof(T));
const uint8_t* cHost = reinterpret_cast<const uint8_t*> (&hostVar);
uint8_t* swapped = new uint8_t[sizeof(T)];
for(uint8_t i=0; i < sizeof(T) ;++i)
{
std::cout << "cHost: " << cHost[swappingSequences[i]] << std::endl;
swapped[i] = cHost[swappingSequences[i]];
}
return *reinterpret_cast<T*> (swapped);
}
void Print()
{
for(int i=0; i < swappingSequences.size() ;++i)
{
std::cout << (int)swappingSequences[i] << " ";
}
std::cout << std::endl;
}
static ByteSwapSequence<T> getBigEndianSwap()
{
BOrder seq;
for(int i=sizeof(T)-1; i != -1 ;--i) { seq.push_back(i); }
return ByteSwapSequence(seq);
}
static ByteSwapSequence<T> getLittleEndianSwap()
{
BOrder seq;
for(uint8_t i=0; i < sizeof(T) ;++i) { seq.push_back(i); }
return ByteSwapSequence(seq);
}
private:
BOrder swappingSequences;
};
/// \brief Check for byte swap sequence to get to a predefined byte order <REWORD>
/// Given a constant with known byteorder, now called "network byte order", this
/// function finds out how the bytes of check have to be swapped to make it equal
/// to constant, to order the bytes in network byte order.
/// \param[in] constant A constante with a known byte order
/// \param[in] check A variable or constant with unknown byte order
/// \return The sequence in which the bytes of check have to be swapped to make it
/// the same byte order as constant, contained in a ByteSwapSequence class
template <typename T>
ByteSwapSequence<T> findSwapSequence(const T constant, const T check)
{
// If the numbers already match we dont have to do anything, yey :-)
if(constant == check)
return ByteSwapSequence<T>();
const uint8_t* cCheck = reinterpret_cast<const uint8_t*> (&check);
typename ByteSwapSequence<T>::BOrder byteNumberArray;
for(size_t i=0; i < sizeof(T) ;++i) { byteNumberArray.push_back(i); }
// Permutate the byte order until constant and check match
do
{
uint8_t* checkCopy = new uint8_t[sizeof(T)];
memcpy((void*)checkCopy, (void*)&check, sizeof(T));
for(size_t i=0; i < byteNumberArray.size() ;++i)
{
checkCopy[i] = cCheck[byteNumberArray[i]];
}
delete[] checkCopy;
if(constant == *reinterpret_cast<T*> (checkCopy))
return ByteSwapSequence<T>(byteNumberArray);
} while(next_permutation(byteNumberArray.begin(), byteNumberArray.end()));
return ByteSwapSequence<T>();
}
template<typename T>
ByteSwapSequence<T> autoDetectSwapSequence()
{
// A known big endian value is written into magicBigEndian
T magicBigEndian = 0;
for(uint8_t i=0; i < sizeof(T) ;++i)
{
reinterpret_cast<uint8_t*>(magicBigEndian)[i] = i;
}
// A platform dependent integral constant is written into nativeNumber
/// TODO: Check if this works on other platforms
T nativeNumber = 66051;
// If the platform is not BigEndian nativeNumber is going to be different from
// magicBigEndian. Next swap the byte order as long as the numbers dont match
ByteSwapSequence<T> swapper = findSwapSequence(magicBigEndian, nativeNumber);
return swapper;
}
/// \brief Print var as binary, pass options to customize output
/// \param[in] var The variable to be printed as binary
/// \param[in] flags Or'ed flags from printBinary_opt
template <typename T>
void printBinary(T var, int flags = kPB_NO_OPT)
{
T swapped_var = var;
if(!(flags & kPB_BIG_ENDIAN || flags & kPB_LITTLE_ENDIAN))
{
swapped_var = autoDetectSwapSequence<T>().networkToHost(var);
}
else
{
if(flags & kPB_BIG_ENDIAN)
{
swapped_var =
ByteSwapSequence<T>::getBigEndianSwap().networkToHost(var);
}
else if(flags & kPB_LITTLE_ENDIAN)
{
swapped_var =
ByteSwapSequence<T>::getLittleEndianSwap().networkToHost(var);
}
}
uint8_t* var_ptr = reinterpret_cast<uint8_t*>(&swapped_var);
for (unsigned int iByte=0; iByte < sizeof(T) ;++iByte)
{
printBinaryByte(var_ptr[iByte]);
if(flags & kPB_GROUP) std::cout << " ";
}
std::cout << std::endl;
}
/// \brief print the bits in a byte
void printBinaryByte(uint8_t byte)
{
uint8_t mask = 128;
for(uint8_t iBit=0; iBit < 8 ;++iBit)
{
if(mask & byte)
std::cout << "1";
else
std::cout << "0";
if(mask == 1)
mask = 128;
else
mask = mask >> 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment