//  Boost endian.hpp header file (proposed) ----------------------------------//

//  (C) Copyright Darin Adler 2000
//  (C) Copyright Beman Dawes 2006

//  Use, modification, and distribution is subject to the Boost Software
//  License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
//  at http://www.boost.org/LICENSE_1_0.txt)

//  See library home page at http://www.boost.org/libs/endian

//----------------------------------------------------------------------------//

//  Original design developed by Darin Adler based on classes developed by Mark
//  Borgerding. Four original class templates combined into a single endian
//  class template by Beman Dawes, who also added the unrolled_byte_loops sign
//  partial specialization to correctly extend the sign when cover integer size
//  differs from endian representation size.

#ifndef BOOST_ENDIAN_HPP
#define BOOST_ENDIAN_HPP

#include <boost/detail/endian.hpp>
#include <boost/cast.hpp>
#include <boost/integer_cover_operators.hpp>
#include <boost/type_traits/is_signed.hpp>
#include <boost/integer.hpp>
#include <boost/cstdint.hpp>
#include <boost/static_assert.hpp>
#include <cstring>
#include <climits>
#include <iosfwd>

namespace boost
{
  namespace detail
  {
    // Unrolled loops for loading and storing streams of bytes.

    template <typename T, std::size_t n_bytes, bool sign=boost::is_signed<T>::value >
    struct unrolled_byte_loops
    {
      typedef unrolled_byte_loops<T, n_bytes - 1, sign> next;

      static T load_big(const unsigned char* bytes)
        { return *(bytes - 1) | (next::load_big(bytes - 1) << 8); }
      static T load_little(const unsigned char* bytes)
        { return *bytes | (next::load_little(bytes + 1) << 8); }

      static void store_big(char* bytes, T value)
        {
          *(bytes - 1) = static_cast<char>(value);
          next::store_big(bytes - 1, value >> 8);
        }
      static void store_little(char* bytes, T value)
        {
          *bytes = static_cast<char>(value);
          next::store_little(bytes + 1, value >> 8);
        }
    };

    template <typename T>
    struct unrolled_byte_loops<T, 1, false>
    {
      static T load_big(const unsigned char* bytes)
        { return *(bytes - 1); }
      static T load_little(const unsigned char* bytes)
        { return *bytes; }
      static void store_big(char* bytes, T value)
        { *(bytes - 1) = static_cast<char>(value); }
      static void store_little(char* bytes, T value)
        { *bytes = static_cast<char>(value); }

    };

    template <typename T>
    struct unrolled_byte_loops<T, 1, true>
    {
      static T load_big(const unsigned char* bytes)
        { return *reinterpret_cast<const signed char*>(bytes - 1); }
      static T load_little(const unsigned char* bytes)
        { return *reinterpret_cast<const signed char*>(bytes); }
      static void store_big(char* bytes, T value)
        { *(bytes - 1) = static_cast<char>(value); }
      static void store_little(char* bytes, T value)
        { *bytes = static_cast<char>(value); }
    };

    template <typename T, std::size_t n_bytes>
    inline
    T load_big_endian(const void* bytes)
    {
      return unrolled_byte_loops<T, n_bytes>::load_big
        (static_cast<const unsigned char*>(bytes) + n_bytes);
    }

    template <typename T, std::size_t n_bytes>
    inline
    T load_little_endian(const void* bytes)
    {
      return unrolled_byte_loops<T, n_bytes>::load_little
        (static_cast<const unsigned char*>(bytes));
    }

    template <typename T, std::size_t n_bytes>
    inline
    T load_native_endian(const void* bytes)
    {
        T value = 0;
#ifdef BOOST_BIG_ENDIAN
        memcpy( reinterpret_cast<char*>(&value) + (sizeof(T) - n_bytes),
                bytes,
                n_bytes );
#else
        memcpy( &value, bytes, n_bytes );
#endif
        if ( boost::is_signed<T>::value && sizeof(T) != n_bytes ) 
        {
    // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
            T const mask = T(1) << ( n_bytes * CHAR_BIT - 1 );
            value |= -(value & mask);
        }
        return value;
    }

    template <typename T, std::size_t n_bytes>
    inline
    void store_big_endian(void* bytes, T value)
    {
      unrolled_byte_loops<T, n_bytes>::store_big
        (static_cast<char*>(bytes) + n_bytes, value);
    }

    template <typename T, std::size_t n_bytes>
    inline
    void store_little_endian(void* bytes, T value)
    {
      unrolled_byte_loops<T, n_bytes>::store_little
        (static_cast<char*>(bytes), value);
    }

    template <typename T, std::size_t n_bytes>
    inline
    void store_native_endian(void* bytes, T value)
    {
#ifdef BOOST_BIG_ENDIAN
        memcpy( bytes,
                reinterpret_cast<char*>(&value) + (sizeof(T) - n_bytes),
                n_bytes );
#else
        memcpy( bytes, &value, n_bytes );
#endif
    }

  } // namespace detail

  //  endian class template and specializations  -----------------------------//

  enum endianness { big, aligned_big,
                    little, aligned_little,
                    native, aligned_native };

  template <endianness E, typename T, std::size_t n_bytes = sizeof(T)>
  class endian;

  //  Specializations that represent unaligned bytes.
  //  Taking an integer type as a parameter provides a nice way to pass both the
  //  size and signedness of the desired integer and get the appropriate
  //  corresponding integer type for the interface.

  template <typename T, std::size_t n_bytes>
  class endian< big, T, n_bytes >
    : integer_cover_operators< endian< big, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes <= sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
      endian(T i) { detail::store_big_endian<T, n_bytes>(bytes, i); }
      operator T() const 
        { return detail::load_big_endian<T, n_bytes>(bytes); }
    private:
  	  char bytes[n_bytes];
  };

  template <typename T, std::size_t n_bytes>
  class endian< little, T, n_bytes >
    : integer_cover_operators< endian< little, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes <= sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
      endian(T i) { detail::store_little_endian<T, n_bytes>(bytes, i); }
      operator T() const
        { return detail::load_little_endian<T, n_bytes>(bytes); }
    private:
  	  char bytes[n_bytes];
  };

  template <typename T, std::size_t n_bytes>
  class endian< native, T, n_bytes >
    : integer_cover_operators< endian< native, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes <= sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
      endian(T i) { detail::store_native_endian<T, n_bytes>(bytes, i); }
      operator T() const
        { return detail::load_native_endian<T, n_bytes>(bytes); }
    private:
  	  char bytes[n_bytes];
  };

  //  Specializations that mimic integer types.
  //  These typically have the same alignment as the underlying types.

  template <typename T, std::size_t n_bytes>
  class endian< aligned_big, T, n_bytes  >
    : integer_cover_operators< endian< aligned_big, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes == sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
  #ifdef BOOST_BIG_ENDIAN
      endian(T i) : integer(i) { }
      operator T() const { return integer; }
  #else
      endian(T i) { detail::store_big_endian<T, sizeof(T)>(&integer, i); }
      operator T() const
        { return detail::load_big_endian<T, sizeof(T)>(&integer); }
  #endif
    private:
  	  T integer;
  };

  template <typename T, std::size_t n_bytes>
  class endian< aligned_little, T, n_bytes  >
    : integer_cover_operators< endian< aligned_little, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes == sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
  #ifdef BOOST_LITTLE_ENDIAN
      endian(T i) : integer(i) { }
      operator T() const { return integer; }
  #else
      endian(T i) { detail::store_little_endian<T, sizeof(T)>(&integer, i); }
      operator T() const
        { return detail::load_little_endian<T, sizeof(T)>(&integer); }
  #endif
    private:
  	  T integer;
  };

  template <typename T, std::size_t n_bytes>
  class endian< aligned_native, T, n_bytes  >
    : integer_cover_operators< endian< aligned_native, T, n_bytes >, T >
  {
      BOOST_STATIC_ASSERT( n_bytes == sizeof(T) );
    public:
      typedef T value_type;
      endian() {}
      endian(T i) : integer(i) { }
      operator T() const { return integer; }
    private:
  	  T integer;
  };

//  naming convention typedefs  ----------------------------------------------//

  // unaligned big endian integer types
  typedef endian< big, int_least8_t, 1 >          big1_t;
  typedef endian< big, int_least16_t, 2 >         big2_t;
  typedef endian< big, int_least32_t, 3 >         big3_t;
  typedef endian< big, int_least32_t, 4 >         big4_t;
  typedef endian< big, int_least64_t, 5 >         big5_t;
  typedef endian< big, int_least64_t, 6 >         big6_t;
  typedef endian< big, int_least64_t, 7 >         big7_t;
  typedef endian< big, int_least64_t, 8 >         big8_t;

  // unaligned big endian unsigned integer types
  typedef endian< big, uint_least8_t, 1 >         ubig1_t;
  typedef endian< big, uint_least16_t, 2 >        ubig2_t;
  typedef endian< big, uint_least32_t, 3 >        ubig3_t;
  typedef endian< big, uint_least32_t, 4 >        ubig4_t;
  typedef endian< big, uint_least64_t, 5 >        ubig5_t;
  typedef endian< big, uint_least64_t, 6 >        ubig6_t;
  typedef endian< big, uint_least64_t, 7 >        ubig7_t;
  typedef endian< big, uint_least64_t, 8 >        ubig8_t;

  // unaligned little endian integer types
  typedef endian< little, int_least8_t, 1 >       little1_t;
  typedef endian< little, int_least16_t, 2 >      little2_t;
  typedef endian< little, int_least32_t, 3 >      little3_t;
  typedef endian< little, int_least32_t, 4 >      little4_t;
  typedef endian< little, int_least64_t, 5 >      little5_t;
  typedef endian< little, int_least64_t, 6 >      little6_t;
  typedef endian< little, int_least64_t, 7 >      little7_t;
  typedef endian< little, int_least64_t, 8 >      little8_t;

  // unaligned little endian unsigned integer types
  typedef endian< little, uint_least8_t, 1 >      ulittle1_t;
  typedef endian< little, uint_least16_t, 2 >     ulittle2_t;
  typedef endian< little, uint_least32_t, 3 >     ulittle3_t;
  typedef endian< little, uint_least32_t, 4 >     ulittle4_t;
  typedef endian< little, uint_least64_t, 5 >     ulittle5_t;
  typedef endian< little, uint_least64_t, 6 >     ulittle6_t;
  typedef endian< little, uint_least64_t, 7 >     ulittle7_t;
  typedef endian< little, uint_least64_t, 8 >     ulittle8_t;

  // unaligned native endian integer types
  typedef endian< native, int_least8_t, 1 >       native1_t;
  typedef endian< native, int_least16_t, 2 >      native2_t;
  typedef endian< native, int_least32_t, 3 >      native3_t;
  typedef endian< native, int_least32_t, 4 >      native4_t;
  typedef endian< native, int_least64_t, 5 >      native5_t;
  typedef endian< native, int_least64_t, 6 >      native6_t;
  typedef endian< native, int_least64_t, 7 >      native7_t;
  typedef endian< native, int_least64_t, 8 >      native8_t;

  // unaligned native endian unsigned integer types
  typedef endian< native, uint_least8_t, 1 >      unative1_t;
  typedef endian< native, uint_least16_t, 2 >     unative2_t;
  typedef endian< native, uint_least32_t, 3 >     unative3_t;
  typedef endian< native, uint_least32_t, 4 >     unative4_t;
  typedef endian< native, uint_least64_t, 5 >     unative5_t;
  typedef endian< native, uint_least64_t, 6 >     unative6_t;
  typedef endian< native, uint_least64_t, 7 >     unative7_t;
  typedef endian< native, uint_least64_t, 8 >     unative8_t;

#define BOOST_HAS_INT16_T
#define BOOST_HAS_INT32_T
#define BOOST_HAS_INT64_T
  
  //  These types only present if platform has exact size integers:
  //     aligned big endian integer types
  //     aligned big endian unsigned integer types
  //     aligned little endian integer types
  //     aligned little endian unsigned integer types

# if defined(BOOST_HAS_INT16_T)
  typedef endian< aligned_big, int16_t >          abig2_t;
  typedef endian< aligned_big, uint16_t >         aubig2_t;
  typedef endian< aligned_little, int16_t >       alittle2_t;
  typedef endian< aligned_little, uint16_t >      aulittle2_t;
  typedef endian< aligned_native, int16_t >       anative2_t;
  typedef endian< aligned_native, uint16_t >      aunative2_t;
# endif

# if defined(BOOST_HAS_INT32_T)
  typedef endian< aligned_big, int32_t >          abig4_t;
  typedef endian< aligned_big, uint32_t >         aubig4_t;
  typedef endian< aligned_little, int32_t >       alittle4_t;
  typedef endian< aligned_little, uint32_t >      aulittle4_t;
  typedef endian< aligned_native, int32_t >       anative4_t;
  typedef endian< aligned_native, uint32_t >      aunative4_t;
# endif

# if defined(BOOST_HAS_INT64_T)
  typedef endian< aligned_big, int64_t >          abig8_t;
  typedef endian< aligned_big, uint64_t >         aubig8_t;
  typedef endian< aligned_little, int64_t >       alittle8_t;
  typedef endian< aligned_little, uint64_t >      aulittle8_t;
  typedef endian< aligned_native, int64_t >       anative8_t;
  typedef endian< aligned_native, uint64_t >      aunative8_t;
# endif

} // namespace boost

#endif // BOOST_ENDIAN_HPP



