/*
 *
 *  Copyright (C) 2014-2017, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  ofstd
 *
 *  Author:  Jan Schlamelcher
 *
 *  Purpose: Implement alignment specifiers / query operators
 *           similar to C++11's alignas and alignof.
 *
 */

#ifndef OFALIGN_H
#define OFALIGN_H

#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/oftypes.h"

// use native functionality if C++11 is supported
#ifdef HAVE_CXX11

#define OFalignof alignof
// these helper templates automatically resolve the alignment
// if a type is given and pass-through any numeric constant
template<typename T>
constexpr size_t OFalignof_or_identity_template() { return alignof(T); }
template<size_t Size>
constexpr size_t OFalignof_or_identity_template() { return Size; }
#define OFalignof_or_identity(A) OFalignof_or_identity_template<A>()

#elif !defined(DOXYGEN) // fallback implementations

// alignof
#ifdef HAVE_GNU_ALIGNOF
#define OFalignof __alignof__
#elif defined(HAVE_MS_ALIGNOF)
#define OFalignof __alignof
#endif

// these helper templates automatically resolve the alignment
// if a type is given and pass-through any numeric constant
template<typename T>
struct OFalignas_size_helper_t
{
#ifdef OFalignof
    Uint8 size[OFalignof(T)];
#else // use sizeof instead
    Uint8 size[sizeof(T)];
#endif
};
template<typename T>
OFalignas_size_helper_t<T> OFalignof_or_identity_template() {}
template<size_t Size>
struct OFalignas_size_helper_s { Uint8 size[Size]; };
template<size_t Size>
OFalignas_size_helper_s<Size> OFalignof_or_identity_template() {}
#define OFalignof_or_identity(A) sizeof(OFalignof_or_identity_template<A>())

#else // DOXYGEN

/** @file ofalign.h
 *  Implements platform independent alignment, if possible similar to
 *  C++11's alignof / alignas functionality.
 */

/** Determine the alignment of a type T.
 *  @note OFalignof may not be available on your platform / compiler combination.
 *    Use <kbd>#ifdef OFalignof</kbd> to query availability.
 *  @details OFalignof behaves similar to
 *  <a href="http://en.cppreference.com/w/cpp/language/alignof">C++11's alignof</a>
 *  when it's supported.
 */
#define OFalignof <unspecified>

/** Align object or class like another type or as specified by an integral expression.
 *  @note OFalignas may not be available on your platform / compiler combination.
 *    Use <kbd>#ifdef OFalignas</kbd> to query availability. See OFalign for an alternative
 *    with limited functionality that supports more platforms in return.
 *  @details OFalignas behaves similar to
 *  <a href="http://en.cppreference.com/w/cpp/language/alignas">C++11's alignas</a>
 *  when it's supported.
 */
#define OFalignas <unspecified>

/** Align a variable or data member.
 *  @note OFalign may not be available on your platform / compiler combination.
 *    Use <kbd>#ifdef OFalign</kbd> to query availability.
 *  @sa OFalign_typename
 *  @details
 *  OFalign can be though of as the least common denominator of the alignment capabilities
 *  available on different platform / compiler combinations. Given a type T and an integral
 *  expression I, <kbd>OFalign(T,I)</kbd> evaluates to a type with a corresponding alignment
 *  modifier (either included in the type definition or as a modifier put next to it).<br>
 *  You may also use another type to specify the desired alignment, e.g. <kbd>OFalign(T,int)</kbd>.
 *  OFalign will then calculate the alignment using <i>OFalignof</i> (if available) or use
 *  <i>sizeof()</i> as approximation.<br>
 *  To align arrays via OFalign, simply pass the array's extents within the parameter, e.g.
 *  <kbd>OFalign(char[12],float)</kbd> to align an array containing <i>12</i> chars like a float.<br>
 *  When using OFalign inside a dependent scope (i.e. inside templates), you may need
 *  to use OFalign_typename instead, e.g.
 *  @code typedef OFalign_typename(T,16) value_type; @endcode
 *  OFalign should support alignments as any power of two <= 8192 (examine your compiler's manual
 *  when in doubt).
 *  Other alignments may also be supported, but may not be available on every platform.
 *  OFalign may simply ignore your request if you specify an unsupported aligment
 *  (won't output an error). You may want to check the alignment via <i>OFalignof</i> in case
 *  it is likely to fail. Using another type as alignment specifier should always work,
 *  as the required alignment is obviously supported in that case
 *  (at least when OFalignof is available).
 */
#define OFalign <unspecified>

/** Alternate version of OFalign to be used within templates.
 *  @note OFalign_typename may not be available on your platform / compiler combination.
 *    Use <kbd>#ifdef OFalign</kbd> or <kbd>#ifdef OFalign_typename</kbd> to query availability.
 *  @sa OFalign
 *  @details
 *  OFalign_typename is an alternate version of OFalign that internally uses keywords like
 *  <i>OFTypename</i> so it can be used inside a dependent scope. See OFalign for more information.
 */
#define OFalign_typename OFTypename <unspecified>

#endif // C++11

// alignas
#if defined(HAVE_CXX11)

#define OFalignas alignas

#elif defined(HAVE_ATTRIBUTE_ALIGNED) && defined(ATTRIBUTE_ALIGNED_SUPPORTS_TEMPLATES)

#define OFalignas(A) __attribute__((aligned(OFalignof_or_identity(A))))

#elif defined(HAVE_DECLSPEC_ALIGN)

// Microsoft workaround: OFalign and OFalign_typename macros
// __declspec(align) does not understand integral expressions
// but instead requires an integral literal, Microsoft says:
// "Valid entries are integer powers of two from 1 to 8192 (bytes),
// such as 2, 4, 8, 16, 32, or 64."
// So this is fundamentally different to the real alignment specifiers
// from GNU and C++11.
#define OFalign(T,A) OFdeclspec_align<T>::as<OFalignof_or_identity(A)>::type
#define OFalign_typename(T,A) OFTypename OFdeclspec_align<T>::template as<OFalignof_or_identity(A)>::type

// The trick / hack: specialize a template for every valid
// integral expression and use an appropriate integral literal to
// define the desired type. At least Microsoft allows us to
// typedef this stuff, but the API is still fundamentally different
// to the favored "OFalignas".
// This also requires a specialization for arrays, since making
// an array from an aligned type is not the same as making an
// aligned array.
#define DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( N )\
template<typename T>\
struct make_type<T,N> { typedef __declspec(align(N)) T type; };\
template<typename T,size_t S>\
struct make_type<T[S],N> { typedef __declspec(align(N)) T type[S]; }

template<typename X>
class OFdeclspec_align
{
    template<typename T,size_t>
    struct make_type { typedef T type; };
    template<typename T,size_t S,size_t A>
    struct make_type<T[S],A> { typedef T type[S]; };
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 1 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 2 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 4 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 8 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 16 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 32 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 64 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 128 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 256 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 512 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 1024 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 2048 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 4096 );
    DCMTK_OFALIGN_HACK_CONSTANT_BY_SPECIALIZATION( 8192 );

public:
    template<size_t A>
    struct as { typedef OFTypename make_type<X,A>::type type; };
};
#endif

#if defined(OFalignas) && !defined(DOXYGEN)
// OFalign based on OFalignas, so this is "platform-independent".
#define OFalign(T,A) OFalignas(A) OFalignas_type<T>::type
#define OFalign_typename(T,A) OFalignas(A) OFTypename OFalignas_type<T>::type
template<typename T>
struct OFalignas_type
{
    typedef T type;
};
template<typename T,size_t S>
struct OFalignas_type<T[S]>
{
    typedef T type[S];
};
#endif

#endif // OFALIGN_H
