// Demonstration program to illustrate the implications of reading a
// sequence of four bytes on little endian and big endian CPUs.  This
// program provides different results on little endian CPUs than it
// does on big endian CPUs.  Note that the input data is carefully
// selected to produce human-readable ASCII values ('0', '1', '2',
// '3') in each byte position and when values are dumped, the value of
// each octet is dumped in order from most significant byte to the
// least significant byte as a list.  A value displayed in parenthesis
// is the numeric value of the complete field.
//
// Written by Bob Friesenhahn <bfriesen@simple.dallas.tx.us> and
// placed in the public domain.

#include <string>
#include <iostream>
#include <inttypes.h>
#include <fstream>

using namespace std;

uint8_t get_uint8_value(const uint32_t value, uint32_t shift)
{
  return ((value >> shift) & 0xFF);
}

uint16_t get_uint16_value(const uint32_t value, uint32_t shift)
{
  return ((value >> shift) & 0xFFFF);
}

void dump_32bits(ostream& stream, const uint32_t& value)
{
  stream
    << get_uint8_value(value, 24)
    << ","
    << get_uint8_value(value, 16)
    << ","
    << get_uint8_value(value, 8)
    << ","
    << get_uint8_value(value, 0);
}

void dump_16bits(ostream& stream, const uint16_t& value)
{
  stream
    << get_uint8_value(value, 8)
    << ","
    << get_uint8_value(value, 0);
}

int main(int /*argc*/,char **/*argv*/)
{
  // Create test file containing four ordered bytes
  const char *testfile = "endiantest.dat";
  ofstream outfile;
  outfile.open(testfile, ios::out | ios::trunc);
  outfile << '0' << '1' << '2' << '3' << endl;
  outfile.close();

  // Test reading four bytes as an array of 8-bit values
  ifstream infile;
  infile.open(testfile);
  char char_values[5];
  infile.read(char_values,4);
  char_values[4]=0;
  infile.close();

  cout << "Four 8-bit values (file order): " << endl;
  for (uint32_t i = 0 ; i < 4; i++)
    {
      cout << "  value[" << i << "] = " << char_values[i] << endl;
    }

  // Test reading four bytes as an array of 16-bit values
  uint16_t short_values[2];
  infile.open(testfile);
  infile.read(reinterpret_cast<char *>(&short_values),4);
  infile.close();

  cout << "Four bytes read as two 16-bit values (MSB to LSB):" << endl;
  for (uint32_t i = 0 ; i < 2; i++)
    {
      cout << "  value[" << i << "] = ";
      dump_16bits( cout, short_values[i] );
      cout << " (" << short_values[i] << ")" << endl;
    }

  // Test reading four bytes as one 32-bit value
  infile.open(testfile);
  uint32_t int_value;
  infile.read(reinterpret_cast<char *>(&int_value),sizeof(int_value));
  infile.close();

  cout << "Four bytes read as one 32-bit value (MSB to LSB): " << endl;
  cout << "  value = ";
  dump_32bits( cout, int_value );
  cout << " (" << int_value << ")" <<endl;

  // Dump out 16-bit values read as part of 32-bit word
  uint16_t short_values_word[2];
  short_values_word[0]=get_uint16_value(int_value, 16);
  short_values_word[1]=get_uint16_value(int_value, 0);
  cout <<  "  16-bit subvalues (most and least significant):" << endl;
  for (uint32_t i = 0 ; i < 2; i++)
    {
      cout << "   value[" << i << "] = ";
      dump_16bits( cout, short_values_word[i] );
      cout << " (" << short_values_word[i] << ")" << endl;
    }

  (void) ::remove(testfile);
  return 0;
}

