/**
* @file perspective_sys.h
* @brief Détection de la plateforme.
* @author Florian Joncour
* @date 2013-2018
* @copyright Ces sources font partie de l'interface de programmation pour la bibliothèque Perspective3D,
un outils de modélisation 3D à partir de vues orthographiques 2D.
Ces sources sont fournies à titre gracieux, l'utilisateur a la liberté de les exploiter
pour créer des applications dérivées à condition de respecter les conditions suivantes:

    1) Ces sources ne peuvent être modifiées.
    2) Le dérivé ne peut être exploité à des fins commerciaux.
    3) Aucune licence commerciale de la bibliothèque dont est tiré ce fichier ne peut être rendue publique.
    4) Toute application exploitant la présente bibliothèque doit explicitement faire mention de l'usage et du rôle de Perspective3D.

Ces conditions peuvent toutefois être modifiées avec l'accord de l'auteur.
*/

#ifndef PERSPECTIVE_SYS_H
#define PERSPECTIVE_SYS_H

#include <cstdint> /* Types de taille fixe */
#include <cstring> /* memset(), memcpy() ... */

#ifndef __cplusplus
#error Compilateur C++ requis !
#endif // __cplusplus

/* Détection du système d'exploitation. */
#if defined(linux) || defined(__linux) || defined(__linux__)
    #define PSYS_LINUX /* Linux */
    #define PSYS_POSIX
#endif // linux

#if defined(WIN32) || defined(_WIN32) || defined(__WINDOWS__) || defined(__TOS_WIN__) || defined(PREDEF_COMPILER_VISUALC) || defined(__MINGW32__) || defined(__CYGWIN__)
    #define PSYS_WINDOWS /* Windows */
    #include "windows.h"
    #include "winnt.h" // Pour les macros spécifiques à l'architecture.
    #ifdef _MSC_VER
        #define PSYS_WIN_MSVC
    #endif // _MSC_VER

    #ifdef __MINGW32__
        #define PSYS_MINGW
    #endif // __MINGW32__

    #ifdef __MINGW32__
        #define PSYS_CYGWIN
    #endif // __CYGWIN__

#endif // WIN32

#if defined(__ANDROID__)
    #define PSYS_ANDROID
    #include <android/api-level.h> /* Nécessaire pour avoir __ANDROID_API__ */
//    #define PSYS_POSIX /* Nécessite le NDK */
#endif // __ANDROID__

#if defined(__BEOS__)
    #define PSYS_BEOS /* BEOS */
    #define PSYS_POSIX
#endif // __BEOS__

#if defined(_AIX)
    #define PSYS_AIX
    #define PSYS_POSIX
#endif // _AIX

#if defined (__hpux)
    #define PSYS_HP_UX
    #define PSYS_POSIX
#endif // __hpux

#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
    #include <sys/param.h>
    #if defined(BSD)
        #define PSYS_BSD
        #if defined(__FreeBSD__)
            #define PSYS_FREEBSD /* FreeBSD */
        #elif defined(__OpenBSD__)
            #define PSYS_OPENBSD /* OpenBSD */
        #elif defined(__NetBSD__)
            #define PSYS_NETBSD /* NetBSD */
        #elif defined(__DragonFly__)
            #define PSYS_DRAGONFLY_BSD /* DragonFly BSD */
        #endif // __FreeBSD__
    #define PSYS_POSIX
    #endif // BSD
#endif // __unix__

#if defined(__sun) && defined(__SVR4)
    #define PSYS_SOLARIS
#endif // __sun

#if (defined(__APPLE__) && defined(__MACH__))
//    #ifdef __MACOSX__
//        #define PSYS_MACOSX
//    #endif // __MACOSX__

    #include <TargetConditionals.h>
    #if TARGET_IPHONE_SIMULATOR == 1 /* iOS dans Xcode */
        #define PSYS_IOS
    #elif TARGET_OS_IPHONE == 1 /* iOS sur iPhone, iPad, etc. */
        #define PSYS_IOS
    #elif TARGET_OS_MAC == 1 /* OSX */
        #define PSYS_MACOSX
        #define PSYS_POSIX
    #endif // TARGET_IPHONE_SIMULATOR
#endif // __APPLE__

/* Détection du compilateur. */
#if defined(_MSC_VER)
    #define PSYS_CXX_MSVC
    #include <intrin.h>
#elif defined(__IBMC__) || defined(__IBMCPP__)
    #define PSYS_CXX_IBM_XL
#elif defined(__PGI)
    #define PSYS_CXX_PGC
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
    #define PSYS_CXX_SOLARIS_STUDIO
#elif defined(__CC_ARM)
    #define PSYS_CXX_ARM_COMPILER
#elif defined(__BORLANDC__) || defined(__CODEGEARC__)
    #define PSYS_CXX_CPPBUILDER
#elif defined(__DMC__)
    #define PSYS_CXX_DMD
#elif defined(__OPEN64__) || defined(__OPENCC__)
    #define PSYS_CXX_OPEN64
#elif defined(__ICC) || defined(__INTEL_COMPILER) /* A définir avant le contrôle __GNUC__, ICC le défini également ! */
    #define PSYS_CXX_ICC
#elif defined(__clang__) /* Même topo, Clang définit __GNUC__ */
    #define PSYS_CXX_CLANG
    #include <features.h>
#elif defined(__GNUC__)
    #define PSYS_CXX_GCC
    #ifdef PSYS_LINUX
        #include <features.h>
    #endif // PSYS_LINUX
#endif // _MSC_VER

/* GlibC */
#if defined(__GNU_LIBRARY__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
    #define PSYS_GLIBC
#endif // __GNU_LIBRARY__

/* Architecture X86 */
#if defined(i386) || defined(__i386) || defined(__i386__) || \
    defined(__i486__) || defined(__i586__) || defined(__i686__) || \
    defined(__X86__) || defined(_X86_) || defined(__I86__) || \
    defined(__IA32__) || defined (_M_IX86) || defined(__THW_INTEL__) || defined(__INTEL__)
#define PARCH_X86
#endif // __i386__

/* Architecture X86_64 */
#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)
    #define PARCH_X86_64
#endif // __amd64__

/* ARM */
#if defined(__arm__) || defined(__arm) || defined(_M_ARM) || defined(_ARM) || defined(__TARGET_ARCH_ARM)
    #define PARCH_ARM
#endif // __arm__

/* ARM Thumb */
#if defined(__thumb__) || defined(_M_ARMT) || defined(__TARGET_ARCH_THUMB)
    #define PARCH_ARM_THUMB
#endif // __thumb__

/* Itanium 64 */
#if defined(__ia64) || defined(__itanium__) || defined(_M_IA64)
    #define PARCH_IA64
#endif // __ia64

/* PowerPC */
#if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
    #define PARCH_POWERPC
    #if defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || defined(__64BIT__) || defined(_LP64) || defined(__LP64__)
        #define PARCH_POWERPC_64
    #else
        #define PARCH_POWERPC_32
    #endif // __powerpc64__
#endif // __powerpc__

/* SPARC */
#if defined(__sparc)
    #define PARCH_SPARC
#endif // __sparc

/* Restriction de la plate-forme */
#if !defined(PSYS_LINUX) && !defined(PSYS_WINDOWS) && !defined(PSYS_MACOSX)
    #error Plateforme non supportée.
#endif // PSYS_LINUX ...

/* Compatible PC. */
#if defined(PARCH_X86) || defined(PARCH_X86_64)
    #define PARCH_X86_X64
#else
    #error Architecture non supportée.
#endif // PARCH_X86

#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
    defined(PARCH_X86_X64) || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(__MIPSEL__)
    #define PARCH_LITTLE_ENDIAN
#elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || \
    defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(__MIPSEB__)
    #define PARCH_BIG_ENDIAN
#endif // __BYTE_ORDER__

#if defined(PARCH_LITTLE_ENDIAN) && defined(PARCH_BIG_ENDIAN)
    #error "PARCH_LITTLE_ENDIAN && PARCH_BIG_ENDIAN ???"
#endif // PARCH_LITTLE_ENDIAN && PARCH_BIG_ENDIAN

#if 1 /* Force la détection du boutisme */
    #if !defined(PARCH_LITTLE_ENDIAN) && !defined(PARCH_BIG_ENDIAN)
        #error "!PARCH_LITTLE_ENDIAN && !PARCH_BIG_ENDIAN ???"
    #endif // !PARCH_LITTLE_ENDIAN && !PARCH_BIG_ENDIAN
#endif // 1

#if defined (PSYS_ANDROID) /* Avant Linux !!! */
    #if __ANDROID_API__ == 1
        #define PSYS_ANDROID_VERSION_STR "1.0"
    #elif __ANDROID_API__ == 2
        #define PSYS_ANDROID_VERSION_STR "1.1"
    #elif __ANDROID_API__ == 3
        #define PSYS_ANDROID_VERSION_STR "1.5"
    #elif __ANDROID_API__ == 4
        #define PSYS_ANDROID_VERSION_STR "1.6"
    #elif __ANDROID_API__ == 5
        #define PSYS_ANDROID_VERSION_STR "2.0"
    #elif __ANDROID_API__ == 6
        #define PSYS_ANDROID_VERSION_STR "2.0.1"
    #elif __ANDROID_API__ == 7
        #define PSYS_ANDROID_VERSION_STR "2.1"
    #elif __ANDROID_API__ == 8
        #define PSYS_ANDROID_VERSION_STR "2.2"
    #elif __ANDROID_API__ == 9
        #define PSYS_ANDROID_VERSION_STR "2.3"
    #elif __ANDROID_API__ == 10
        #define PSYS_ANDROID_VERSION_STR "2.3.3"
    #elif __ANDROID_API__ == 11
        #define PSYS_ANDROID_VERSION_STR "3.0"
    #elif __ANDROID_API__ == 12
        #define PSYS_ANDROID_VERSION_STR "3.1"
    #elif __ANDROID_API__ == 13
        #define PSYS_ANDROID_VERSION_STR "3.2"
    #elif __ANDROID_API__ == 14
        #define PSYS_ANDROID_VERSION_STR "4.0"
    #elif __ANDROID_API__ == 15
        #define PSYS_ANDROID_VERSION_STR "4.0.3"
    #elif __ANDROID_API__ == 16
        #define PSYS_ANDROID_VERSION_STR "4.1"
    #elif __ANDROID_API__ == 17
        #define PSYS_ANDROID_VERSION_STR "4.2"
    #elif __ANDROID_API__ == 18
        #define PSYS_ANDROID_VERSION_STR "4.3"
    #elif __ANDROID_API__ == 19
        #define PSYS_ANDROID_VERSION_STR "4.4"
    #elif __ANDROID_API__ == 20
        #define PSYS_ANDROID_VERSION_STR "4.4W"
    #elif __ANDROID_API__ == 21
        #define PSYS_ANDROID_VERSION_STR "5.0"
    #elif __ANDROID_API__ == 22
        #define PSYS_ANDROID_VERSION_STR "5.1"
    #elif __ANDROID_API__ == 23
        #define PSYS_ANDROID_VERSION_STR "6.0"
    #elif __ANDROID_API__ == 24
        #define PSYS_ANDROID_VERSION_STR "7.0"
    #elif __ANDROID_API__ == 25
        #define PSYS_ANDROID_VERSION_STR "7.1"
    #else
        #define PSYS_ANDROID_VERSION_STR "> 7.1"
    #endif // __ANDROID_API__
    #define PSYS_PLATEFORME_STR "Android " PSYS_ANDROID_VERSION_STR
#elif defined(PSYS_LINUX)
    #ifdef PSYS_GLIBC
        #define PSYS_PLATEFORME_STR "GNU/Linux"
    #else
        #define PSYS_PLATEFORME_STR "Linux"
    #endif // PSYS_GLIBC
#elif defined(PSYS_WINDOWS)
    #define PSYS_PLATEFORME_STR "Windows"
#elif defined(PSYS_MACOSX)
    #define PSYS_PLATEFORME_STR "Mac OS X"
#elif defined PSYS_IOS
    #define PSYS_PLATEFORME_STR "iOS"
#elif defined(PSYS_FREEBSD)
    #define PSYS_PLATEFORME_STR "FreeBSD"
#elif defined(PSYS_OPENBSD)
    #define PSYS_PLATEFORME_STR "OpenBSD"
#elif defined(PSYS_NETBSD)
    #define PSYS_PLATEFORME_STR "NetBSD"
#elif defined(PSYS_DRAGONFLY_BSD)
    #define PSYS_PLATEFORME_STR "DragonFly BSD"
#elif defined(PSYS_BEOS)
    #define PSYS_PLATEFORME_STR "BeOS"
#elif defined(PSYS_SOLARIS)
    #define PSYS_PLATEFORME_STR "Solaris"
#elif defined(PSYS_HP_UX)
    #define PSYS_PLATEFORME_STR "HP-UX"
#elif defined(PSYS_AIX)
    #define PSYS_PLATEFORME_STR "IBM AIX"
#else
    /**
    * @brief Nom du système d'exploitation sous forme de chaine de caractère.
    */
    #define PSYS_PLATEFORME_STR "~"
#endif // PSYS_ANDROID

#if defined(PARCH_X86)
    #define PSYS_ARCH_STR "x86"
#elif defined(PARCH_X86_64)
    #define PSYS_ARCH_STR "x86_64"
#elif defined(PARCH_IA64)
    #define PSYS_ARCH_STR "IA64"
#elif defined(PARCH_POWERPC)
    #define PSYS_ARCH_STR "PPC"
#elif defined(PARCH_SPARC)
    #define PSYS_ARCH_STR "SPARC"
#elif defined (PARCH_ARM)
    #define PSYS_ARCH_STR "ARM"
#elif defined(PARCH_ARM_THUMB)
    #define PSYS_ARCH_STR "ARM Thumb"
#else
    /**
    * @brief Architecture du processeur sous forme de chaine de caractère.
    */
    #define PSYS_ARCH_STR "~"
#endif // PARCH_X86

#if defined(PSYS_CXX_GCC)
    #define PSYS_COMPILATEUR_STR "GCC"
#elif defined(PSYS_CXX_CLANG)
    #define PSYS_COMPILATEUR_STR "Clang"
#elif defined(PSYS_CXX_ICC)
    #define PSYS_COMPILATEUR_STR "Intel C++"
#elif defined(PSYS_CXX_MSVC)
    #define PSYS_COMPILATEUR_STR "Microsoft Visual C++"
#elif defined(PSYS_CXX_PGC)
    #define PSYS_COMPILATEUR_STR "PGC/PGC++"
#elif defined(PSYS_CXX_IBM_XL)
    #define PSYS_COMPILATEUR_STR "IBM XL"
#elif defined(PSYS_CXX_SOLARIS_STUDIO)
    #define PSYS_COMPILATEUR_STR "Solaris Studio"
#elif defined(PSYS_CXX_ARM_COMPILER)
    #define PSYS_COMPILATEUR_STR "ARM Compiler"
#elif defined(PSYS_CXX_CPPBUILDER)
    #define PSYS_COMPILATEUR_STR "Borland C++"
#elif defined(PSYS_CXX_DMD)
    #define PSYS_COMPILATEUR_STR "Digital Mars C++"
#elif defined (PSYS_CXX_OPEN64)
    #define PSYS_COMPILATEUR_STR "Open64"
#else
    /**
    * @brief Nom du compilateur détecté sous forme de chaine de caractères.
    */
    #define PSYS_COMPILATEUR_STR "~"
#endif // PSYS_CXX_GCC

#define PSYS_CPP_VERSION __cplusplus

#if (PSYS_CPP_VERSION >= 201703L)
    #define PSYS_CPP17
#endif // PSYS_CPP_VERSION
#if (PSYS_CPP_VERSION >= 201402L)
    #define PSYS_CPP14
#endif // PSYS_CPP_VERSION
#if (PSYS_CPP_VERSION >= 201103L)
    #define PSYS_CPP11
#endif // PSYS_CPP_VERSION
#if (PSYS_CPP_VERSION >= 199711L)
    #define PSYS_CPP98
#endif // PSYS_CPP_VERSION

#if !defined(PSYS_CPP11)
    #define nullptr ((void*) 0)
#endif // PSYS_CPP11

/**
* @brief Qualificatif variable locale à un thread (GCC, Clang ou MSVC).
*/
#if !defined(PSYS_CPP11) && !defined(thread_local)
    #if (defined(PSYS_CXX_GCC) && defined(_GLIBCXX_HAVE_TLS)) || defined(PSYS_CXX_CLANG)
        #define PSYS_TLS __thread
    #elif defined(PSYS_CXX_MSVC)
        #define PSYS_TLS __declspec(thread)
    #else
        #ifdef PSYS_CXX_ICC
            #ifdef PSYS_LINUX
                #define PSYS_TLS __thread
            #elif defined(PSYS_WINDOWS)
                #define PSYS_TLS __declspec(thread)
            #else
                #define PSYS_TLS
            #endif // PSYS_LINUX
        #else
            #define PSYS_TLS
        #endif // PSYS_CXX_ICC
    #endif // PSYS_CXX_GCC
#else
    #define PSYS_TLS thread_local
#endif // PSYS_CPP11

/**
* @brief Mot-clé restrict (du C99)
*/
#if (defined(PSYS_CXX_GCC) || defined(PSYS_CXX_CLANG)) && !defined(PSYS_MINGW) // MinGW a quelques problèmes pour le moment avec __restrict__ ...
    #define __restrict__ PSYS_restrict
#elif defined(PSYS_CXX_MSVC)
    #define __restrict PSYS_restrict
#else
    #define PSYS_restrict
#endif // PSYS_CXX_GCC ...

#undef F_INLINE
#undef F_CONST
#undef F_PURE
#undef P_UNUSED
#undef F_DEOPTIM

#ifdef PSYS_CXX_GCC
    #define F_DEOPTIM __attribute__((optimize("O0")))
#else
    /**
    * @brief Désactive l'optimisation du compilateur sur une fonction (GCC).
    */
    #define F_DEOPTIM
#endif // PSYS_CXX_GCC

#if defined(PSYS_CXX_GCC)
    #define F_INLINE static inline __attribute__ ((always_inline)) /* Fonctions */
    #define M_INLINE inline __attribute__ ((always_inline)) /* Méthodes */
    #define F_CONST  __attribute__ ((const))
    #define F_PURE   __attribute__ ((pure))
    #define likely(x)      __builtin_expect(!!(x), 1)
    #define unlikely(x)    __builtin_expect(!!(x), 0)
#elif defined(PSYS_CXX_CLANG)
    #define F_INLINE static inline __attribute__ ((always_inline))
    #define M_INLINE inline __attribute__ ((always_inline))
    #define F_CONST  __attribute__ ((const))
    #define F_PURE
    #define likely(x)      __builtin_expect(!!(x), 1)
    #define unlikely(x)    __builtin_expect(!!(x), 0)
#elif defined(PSYS_CXX_MSVC)
    #define F_INLINE static inline
    #define M_INLINE __forceinline
    #define F_CONST
    #define F_PURE
    #define likely(x)
    #define unlikely(x)
#else
    /**
    * @brief Attribut de fonction force inline (GCC ou Clang)).
    */
    #define F_INLINE inline

    /**
    * @brief Attribut de méthode force inline (GCC ou Clang)).
    */
    #define M_INLINE inline

    /**
    * @brief Attribut de fonction fonction const (GCC ou Clang).
    */
    #define F_CONST

    /**
    * @brief Attribut de fonction fonction pure (GCC ou Clang).
    */
    #define F_PURE
#endif // PSYS_CXX_GCC

/**
* @brief Attribut de variable inutilisée (empêche le compilateur d'afficher un message d'avertissement).
*/
#define P_UNUSED(X) ((void)(X))

#if !defined(DLL_IMPORT) || !defined(DLL_EXPORT) || !defined(DLL_LOCAL) || !defined(DLL_INTERNAL)
    #undef DLL_IMPORT
    #undef DLL_EXPORT
    #undef DLL_LOCAL
    #undef DLL_INTERNAL

    #ifdef PSYS_WINDOWS // Windows, MinGW ou Cygwin
        #define DLL_IMPORT __declspec(dllimport)
        #define DLL_EXPORT __declspec(dllexport)
        #define DLL_LOCAL
        #define DLL_INTERNAL DLL_LOCAL
    #else
        #if __GNUC__ >= 4
            #define DLL_IMPORT __attribute__ ((visibility ("default")))
            #define DLL_EXPORT __attribute__ ((visibility ("default")))
            #define DLL_LOCAL  __attribute__ ((visibility ("hidden")))
            #define DLL_INTERNAL  __attribute__ ((visibility ("internal"))) /* Attention, la visiblité "internal" peut provoquer des incompatibilités avec l'ABI si du code interne est appelé depuis l'extérieur (via un pointeur de fonction par exemple). */
        #else
            #define DLL_IMPORT
            #define DLL_EXPORT
            #define DLL_LOCAL
            #define DLL_INTERNAL
        #endif // __GNUC__
    #endif // PSYS_WINDOWS
#endif // !DLL_IMPORT || !DLL_EXPORT || !DLL_LOCAL || !DLL_INTERNAL

#if defined(PSYS_CXX_GCC) && defined(PARCH_X86_X64)
    #define FAST_CALL __attribute__ ((fastcall))
#elif defined(PSYS_CXX_MSVC) && defined(PARCH_X86_X64)
    #define FAST_CALL __fastcall
#else
    /**
    * @brief Attribut de fonction "fastcall" (GCC ou MSVC)
    */
    #define FAST_CALL
#endif // PSYS_CXX_GCC && PARCH_X86_X64

#ifdef DLL_API
    #undef DLL_API /* Lors de la compilation du client... */
#endif // DLL_API

#if INTERNE_PERSPECTIVE3D /* Lors de la compilation de la bibliothèque... */
    #define DLL_API DLL_EXPORT
#else
/**
* @brief Attribut de classe ou méthode exportée par l'API (GCC, Clang ou MSVC). Donc importé côté client.
*/
#define DLL_API DLL_IMPORT
#endif // INTERNE_PERSPECTIVE3D

#if defined(PSYS_CPP11)
    #define register      /* Déprécié depuis C++11. */
#endif  // PSYS_CPP11

#define P_INTERDIT_COPIE(CLASSE) CLASSE(const CLASSE &) = delete;\
                                 CLASSE &operator=(const CLASSE &) = delete;

#define P_INTERDIT_COPIE_TEMPLATE(CLASSE, T) CLASSE(const CLASSE<T> &) = delete;\
                                 CLASSE &operator=(const CLASSE<T> &) = delete;

#define P_INTERDIT_COPIE_TEMPLATE2(CLASSE, T1, T2) CLASSE(const CLASSE<T1, T2> &) = delete;\
                                 CLASSE &operator=(const CLASSE<T1, T2> &) = delete;

#define P_INTERDIT_COPIE_TEMPLATE3(CLASSE, T1, T2, T3) CLASSE(const CLASSE<T1, T2, T3> &) = delete;\
                                 CLASSE &operator=(const CLASSE<T1, T2, T3> &) = delete;

#define P_INTERDIT_COPIE_TEMPLATE_N(CLASSE, ...) CLASSE(const CLASSE<__VA_ARGS__> &) = delete;\
                                 CLASSE &operator=(const CLASSE<__VA_ARGS__> &) = delete;

/**
* @brief Attribut 'fallthrough' (standard à partir de C++17) pour les opérations de switch. Permet d'ignorer les avertissements du compilateurs dans les etiquettes 'case' ne contenant pas de 'break'.
* Cela permet d'expliciter au compilateur que l'on souhaite continuer à parcourir le switch sur les étiquettes qui suivent.
*/
#ifdef PSYS_CPP17 /* C++17 */
    #define EXPLICIT_FALLTHROUGH [[fallthrough]]
#elif defined(PSYS_CPP14) || defined(PSYS_CPP11) /* C++14 ou C++11... */
    #if defined(PSYS_CXX_GCC)
        #define EXPLICIT_FALLTHROUGH [[gnu::fallthrough]]
    #elif defined(PSYS_CXX_CLANG)
        #define EXPLICIT_FALLTHROUGH [[clang::fallthrough]]
    #else
        #define EXPLICIT_FALLTHROUGH
    #endif // PSYS_CXX_GCC
#else /* C++03 ou C */
//#define EXPLICIT_FALLTHROUGH
    #if defined(PSYS_CXX_GCC)
        #define EXPLICIT_FALLTHROUGH __attribute__ ((fallthrough))
        //#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
    #elif defined(PSYS_CXX_CLANG)
        #define EXPLICIT_FALLTHROUGH
        #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
    #else
        /**
        * @brief Attribut [[fallthrough]]
        */
        #define EXPLICIT_FALLTHROUGH
    #endif // PSYS_CXX_GCC
#endif // PSYS_CPP17

#ifdef SUPPORT_DL
/**
* @brief Chargement dynamique de librairie (sur Unix, nécessite le liage à la bibliothèque 'dl').
*/
    #ifdef PSYS_WINDOWS
        //#include <Windows.h>
        #define DL_LOAD(name) LoadLibraryA(name)
        #define DL_LOAD_GLOBAL(name) LoadLibraryA(name)
        #define DL_UNLOAD(handle) FreeLibrary((HMODULE) handle)
        #define DL_GETPROC(handle, name) GetProcAddress((HMODULE) handle, name)
        #define DL_HANDLE_T (HMODULE)
        #define DL_PROC_ATTR __stdcall
    #elif defined(PSYS_POSIX)
        #include <dlfcn.h>
        #define DL_LOAD(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
        #define DL_LOAD_GLOBAL(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
        #define DL_UNLOAD(handle) (dlclose(handle) == 0)
        #define DL_GETPROC(handle, name) dlsym(handle, name)
        #define DL_HANDLE_T (void *)
        #define DL_PROC_ATTR
    #else
        #define DL_LOAD(name) (nullptr)
        #define DL_LOAD_GLOBAL(name) (nullptr)
        #define DL_UNLOAD(name) (0)
        #define DL_GETPROC(handle, name) (nullptr)
        #define DL_HANDLE_T (void *)
        #define DL_PROC_ATTR
    #endif // PSYS_WINDOWS
    #define DL_DECLARE(PROTO_RETURN, PROTO_ARGUMENTS, VAR_NAME, DL, FUNC_NAME) PROTO_RETURN (DL_PROC_ATTR *VAR_NAME) PROTO_ARGUMENTS = (PROTO_RETURN (*) PROTO_ARGUMENTS) DL_GETPROC(DL, #FUNC_NAME)
#endif // SUPPORT_DL

/**
* @brief Types entiers.
*/

typedef signed int pint;
typedef unsigned int puint;
typedef signed short pshort;
typedef unsigned short pushort;
typedef signed long long plong;
typedef unsigned long long pulong;

#ifdef PSYS_WIN_MSVC
typedef __int8 pint8;
typedef unsigned __int8 puint8;
typedef __int16 pint16;
typedef unsigned __int16 puint16;
typedef __int32 pint32;
typedef unsigned __int32 puint32;
typedef __int64 pint64;
typedef unsigned __int64 puint64;
#else
typedef int8_t pint8;
typedef uint8_t puint8;
typedef int16_t pint16;
typedef uint16_t puint16;
typedef int32_t pint32;
typedef uint32_t puint32;
typedef int64_t pint64;
typedef uint64_t puint64;
#endif // PSYS_WIN_MSVC

#define Ptest_ptr(PTR) ((PTR) != nullptr)

#ifdef PSYS_CPP11
    #include <utility> /* std::swap, std::move... */
#endif // PSYS_CPP11

/**
 * @brief Espace de nommage des fonctions dépendantes de l'architecture.
 */
namespace PerspectiveSys
{
#ifdef PSYS_CPP11
//     template<typename T> static inline void swap(T& a, T& b)
//     {
//         T c(std::move(a));
//         a = std::move(b);
//         b = std::move(c);
//     }

    template<typename T> static inline void swap(T& a, T& b)
    {
        std::swap<T>(a, b);
    }
#else
    template<typename T> static inline void swap(T& a, T& b)
    {
        T c(a);
        a = b;
        b = c;
    }
#endif // PSYS_CPP11

    template<typename T> static inline void swap_xor(T& a, T& b)
    {
        a ^= b;
        b ^= a;
        a ^= b;
    }

    /**
     * @brief Contrôle l'ordre des octets sur l'architecture actuelle (evalué à l'exécution).
     */
    M_INLINE bool PetitBoutisme() F_CONST;
    bool PetitBoutisme()
    {
#if defined(PARCH_LITTLE_ENDIAN)
        return true;
#elif defined(PARCH_BIG_ENDIAN)
        return false;
#else
        static const pint test_b = 1;
        return ((*reinterpret_cast<const char *>(&test_b)) == 1);
#endif // PARCH_LITTLE_ENDIAN
    }

    /**
     * @brief InverseOcts<type>(var) Inverse l'ordre des octets pour la variable (de type entière) donnée en argument. Renvoi une copie. Cette fonction est bien plus rapide que InverseOcts(), mais elle impose que le type soit une valeur entière du fait de décalages de bits.
     */
    template<typename T> static inline T InverseOctsInt(const T* v)
    {
        T ret;

        switch (sizeof(T))
        {
            case 1: // 8 bits
            {
                return *v;
            }
            case 2: // 16 bits
            {
                ret = *v;
                return (((ret >> 8) & 0xFFU) | ((ret & 0xFFU) << 8));
            }
            case 4: // 32 bits
            {
                ret = *v;
                return (((ret & 0xFF000000U) >> 24) | ((ret & 0x00FF0000U) >>  8) | ((ret & 0x0000FF00U) << 8) | ((ret & 0x000000FFU) << 24));
            }
            case 8: // 64 bits
            {
                ret = *v;
                return (((ret & 0xFF00000000000000ULL) >> 56) | ((ret & 0x00FF000000000000ULL) >> 40) |
                        ((ret & 0x0000FF0000000000ULL) >> 24) | ((ret & 0x000000FF00000000ULL) >>  8) |
                        ((ret & 0x00000000FF000000ULL) <<  8) | ((ret & 0x0000000000FF0000ULL) << 24) |
                        ((ret & 0x000000000000FF00ULL) << 40) | ((ret & 0x00000000000000FFULL) << 56));
            }
            default:
            {
                break;
            }
        }

        /* Inversion générique: */
        char * dest = reinterpret_cast<char *>(&ret);
        char const * source = ((char const *) v) + sizeof(T) - 1;
        for(unsigned long iter = sizeof(T); iter>0; --iter)
        {
            *dest++ = *source--;
        }
        return ret;
    }

    /**
     * @brief InverseOcts<type>(var) Inverse l'ordre des octets (de n'import quel type) pour la variable donnée en argument. Renvoi une copie.
     */
    template<typename T> static inline T InverseOcts(const T* v)
    {
#if 0
        T ret;
        char * dest = ((char *) &ret);
        switch (sizeof(T))
        {
            case 1: // 8 bits
            {
                return *v;
            }
            case 2: // 16 bits
            {
                ret = *v;
                PerspectiveSys::swap(dest[0], dest[1]);
                return ret;
            }
            case 4: // 32 bits
            {
                ret = *v;
                PerspectiveSys::swap(dest[0], dest[3]);
                PerspectiveSys::swap(dest[1], dest[2]);
                return ret;
            }
            case 8: // 64 bits
            {
                ret = *v;
                PerspectiveSys::swap(dest[0], dest[7]);
                PerspectiveSys::swap(dest[1], dest[6]);
                PerspectiveSys::swap(dest[2], dest[5]);
                PerspectiveSys::swap(dest[3], dest[4]);
                return ret;
            }
            default:
            {
                break;
            }
        }
#else // Beaucoup de casts. C'est moins beau, mais plus rapide (particulièrement avec gcc -O2, qui utilise l'instruction bswap à la place des opérations bits à bits. C'est dans ce cas 4x plus rapide)...
        switch (sizeof(T))
        {
            case 1: // 8 bits
            {
                return *v;
            }
            case 2: // 16 bits
            {
                puint16 ret = *reinterpret_cast<const puint16*>(v);
                ret = (((ret >> 8) & 0xFFU) | ((ret & 0xFFU) << 8));
                const T *r = reinterpret_cast<T*>(&ret);
                return *r;
            }
            case 4: // 32 bits
            {
                puint32 ret = *reinterpret_cast<const puint32*>(v);
                ret = (((ret & 0xFF000000U) >> 24) | ((ret & 0x00FF0000U) >>  8) | ((ret & 0x0000FF00U) << 8) | ((ret & 0x000000FFU) << 24));
                const T *r = reinterpret_cast<T*>(&ret);
                return *r;
            }
            case 8: // 64 bits
            {
                puint64 ret = *reinterpret_cast<const puint64*>(v);
                ret =  (((ret & 0xFF00000000000000ULL) >> 56) | ((ret & 0x00FF000000000000ULL) >> 40) |
                        ((ret & 0x0000FF0000000000ULL) >> 24) | ((ret & 0x000000FF00000000ULL) >>  8) |
                        ((ret & 0x00000000FF000000ULL) <<  8) | ((ret & 0x0000000000FF0000ULL) << 24) |
                        ((ret & 0x000000000000FF00ULL) << 40) | ((ret & 0x00000000000000FFULL) << 56));
                const T *r = reinterpret_cast<T*>(&ret);
                return *r;
            }
            default:
            {
                break;
            }
        }
        T ret;
        char * dest = reinterpret_cast<char *>(&ret);
#endif // 0
        /* Inversion générique: */
        char const * source = ((char const *) v) + sizeof(T) - 1;
        for(unsigned long iter = sizeof(T); iter>0; --iter)
        {
            *dest++ = *source--;
        }
        return ret;
    }

    /**
     * @brief Conv_PetitBoutisme<type>(var) Conversion exclusive vers petit-boutisme. Renvoi une copie
     */
    template<typename T> static inline T Conv_PetitBoutisme(const T* v)
    /* Conversion exclusive vers petit-boutisme. */
    {
        if (PetitBoutisme()) // Si architecture petit-boutiste, on touche à rien.
        {
            return *v;
        }
        return InverseOcts<T>(v);
    }

    template<typename T> static inline T Conv_PetitBoutisme(T v)
    {
        return Conv_PetitBoutisme<T>(&v);
    }

    /**
     * @brief Conv_GrandBoutisme<type>(var) Conversion exclusive vers grand-boutisme. Renvoi une copie
     */
    template<typename T> static inline T Conv_GrandBoutisme(const T* v)
    {
        if (!PetitBoutisme()) // Si architecture grand-boutiste, on touche à rien.
        {
            return *v;
        }
        return InverseOcts<T>(v);
    }

    template<typename T> static inline T Conv_GrandBoutisme(T v)
    {
        return Conv_GrandBoutisme<T>(&v);
    }

    /**
     * @brief Conv_PetitBoutismeInt<type>(var) Conversion exclusive d'un type entier vers petit-boutisme. Renvoi une copie.
     */
    template<typename T> static inline T Conv_PetitBoutismeInt(const T* v)
    /* Conversion exclusive vers petit-boutisme. */
    {
        if (PetitBoutisme()) // Si architecture petit-boutiste, on touche à rien.
        {
            return *v;
        }
        return InverseOctsInt<T>(v);
    }

    template<typename T> static inline T Conv_PetitBoutismeInt(T v)
    {
        return Conv_PetitBoutismeInt<T>(&v);
    }

    /**
     * @brief Conv_GrandBoutismeInt<type>(var) Conversion exclusive d'un type entier vers grand-boutisme. Renvoi une copie
     */
    template<typename T> static inline T Conv_GrandBoutismeInt(const T* v)
    {
        if (!PetitBoutisme()) // Si architecture grand-boutiste, on touche à rien.
        {
            return *v;
        }
        return InverseOctsInt<T>(v);
    }

    template<typename T> static inline T Conv_GrandBoutismeInt(T v)
    {
        return Conv_GrandBoutismeInt<T>(&v);
    }

} // namespace PerspectiveSys

#define Pmemset std::memset
#define Pmemcpy std::memcpy
#define Pmemmove std::memmove

#endif // PERSPECTIVE_SYS_H
