/* xspan -- a minimally invasive checked memory smart pointer

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer
   <markus@oberhumer.com>
 */

#pragma once

#if WITH_XSPAN

// namespace
#if 1
#define XSPAN_NAMESPACE_NAME  XSpan
#define XSPAN_NAMESPACE_BEGIN namespace XSPAN_NAMESPACE_NAME {
#define XSPAN_NAMESPACE_END   }
#define XSPAN_NS(x)           XSPAN_NAMESPACE_NAME ::x
#else
#define XSPAN_NAMESPACE_BEGIN /*empty*/
#define XSPAN_NAMESPACE_END   /*empty*/
#define XSPAN_NS(x)           ::x
#endif

XSPAN_NAMESPACE_BEGIN

#if XSPAN_CONFIG_ENABLE_DEBUG
#define XSPAN_DEBUG_ARGS  const XSpanDebugFile &ff,
#define XSPAN_DEBUG_IMPL  f(ff),
#define XSPAN_DEBUG_OTHER f(other.f),
#define XSPAN_DEBUG_PASS  ff,
#else
#define XSPAN_DEBUG_ARGS  XSpanDebugFile,
#define XSPAN_DEBUG_IMPL  /*empty*/
#define XSPAN_DEBUG_OTHER /*empty*/
#define XSPAN_DEBUG_PASS  /*empty*/
#endif

#if XSPAN_CONFIG_ENABLE_DEBUG
struct XSpanDebugFile final {
    const char *src_file;
    size_t src_line;
    static forceinline_constexpr XSpanDebugFile make(const char *f, size_t l) noexcept {
        return XSpanDebugFile(f, l);
    }
    forceinline_constexpr XSpanDebugFile() noexcept : src_file(nullptr), src_line(0) {}
private:
    explicit forceinline_constexpr XSpanDebugFile(const char *f, size_t l) noexcept : src_file(f),
                                                                                      src_line(l) {}
    UPX_CXX_DISABLE_ADDRESS(XSpanDebugFile)
};
#define XSpanDebugFileMake()                                                                       \
    (XSPAN_NS(XSpanDebugFile)(XSPAN_NS(XSpanDebugFile)::make(__FILE__, __LINE__)))
#else
struct XSpanDebugFile final {
    forceinline_constexpr XSpanDebugFile() noexcept {}
    UPX_CXX_DISABLE_ADDRESS(XSpanDebugFile)
};
#endif

// HINT: set env-var "UPX_DEBUG_DOCTEST_DISABLE=1" for improved debugging experience
noreturn void xspan_fail_nullptr() may_throw;
noreturn void xspan_fail_nullbase() may_throw;
noreturn void xspan_fail_not_same_base() may_throw;
noreturn void xspan_fail_range_nullptr() may_throw;
noreturn void xspan_fail_range_nullbase() may_throw;
noreturn void xspan_fail_range_range() may_throw;
void xspan_check_range(const void *ptr, const void *base, ptrdiff_t size_in_bytes) may_throw;

// help constructor to distinguish between number of elements and bytes
struct XSpanCount final {
    explicit forceinline_constexpr XSpanCount(size_t n) noexcept : count(n) {}
    const size_t count; // public
};
struct XSpanSizeInBytes final {
    explicit forceinline_constexpr XSpanSizeInBytes(size_t bytes) noexcept : size_in_bytes(bytes) {}
    const size_t size_in_bytes; // public
};

template <class T>
struct TypeForSizeOf {
    typedef T type;
};
template <>
struct TypeForSizeOf<void> {
    typedef char type;
};
template <>
struct TypeForSizeOf<const void> {
    typedef const char type;
};

template <class T>
struct ValueForSizeOf {
    static const size_t value = sizeof(typename TypeForSizeOf<T>::type);
};

ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<char>::value == 1)
ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<const char>::value == 1)
ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<void>::value == 1)
ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<const void>::value == 1)
ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<int>::value == 4)
ACC_COMPILE_TIME_ASSERT_HEADER(ValueForSizeOf<const int>::value == 4)

#ifndef xspan_mem_size_impl
template <class T>
static inline size_t xspan_mem_size_impl(size_t n) {
#ifdef UPX_VERSION_HEX
    // check for overflow and sane limits
    return mem_size(sizeof(T), n);
#else
    return sizeof(T) * n;
#endif
}
#endif

template <class T>
static inline size_t xspan_mem_size(size_t n) {
    return xspan_mem_size_impl<typename TypeForSizeOf<T>::type>(n);
}

template <class T>
static inline void xspan_mem_size_assert_ptrdiff(ptrdiff_t n) {
    if (n >= 0)
        (void) xspan_mem_size<T>((size_t) n);
    else
        (void) xspan_mem_size<T>((size_t) -n);
}

#if __cplusplus >= 201103L && 0
// unfortunately doesn't work with some older versions of libstdc++
// (TODO later: we now require C++17, so this now probably works on all supported platforms)
template <class From, class To>
struct XSpan_is_convertible : public std::is_convertible<From *, To *> {};
#else
// manual implementation

namespace XSpan_detail {
// XSpan_void_to_T - helper for "void"
template <class T, class U>
struct XSpan_void_to_T {
    typedef U type;
};
template <class T>
struct XSpan_void_to_T<T, void> {
    typedef typename std::remove_const<T>::type type;
};
template <class T>
struct XSpan_void_to_T<T, const void> {
    typedef T type;
};
// XSpan_ptr_is_convertible
template <class From, class To>
struct XSpan_ptr_is_convertible : public std::false_type {};
template <class T>
struct XSpan_ptr_is_convertible<T, T> : public std::true_type {};
template <class T>
struct XSpan_ptr_is_convertible<T, const T> : public std::true_type {};
} // namespace XSpan_detail

template <class From, class To>
struct XSpan_is_convertible : public XSpan_detail::XSpan_ptr_is_convertible<
                                  From, typename XSpan_detail::XSpan_void_to_T<From, To>::type> {};
#endif

#if DEBUG || 1
// need extra parenthesis because the C preprocessor does not understand C++ templates
// char => char
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<char, char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<char, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<const char, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, char>::value))
// void => void
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<void, void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<void, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<const void, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const void, void>::value))
// char => void
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<char, void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<char, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<const char, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, void>::value))
// void => char
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<void, char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<void, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const void, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const void, char>::value))
// byte => void
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<byte, void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<byte, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<const byte, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const byte, void>::value))
// void => byte
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<void, byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<void, const byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const void, const byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const void, byte>::value))
// int => void
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<int, void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<int, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((XSpan_is_convertible<const int, const void>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const int, void>::value))
// char => int
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<char, int>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<char, const int>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, const int>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, int>::value))
// char => byte
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<char, byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<char, const byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, const byte>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const char, byte>::value))
// byte => char
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<byte, char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<byte, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const byte, const char>::value))
ACC_COMPILE_TIME_ASSERT_HEADER((!XSpan_is_convertible<const byte, char>::value))
#endif

/*************************************************************************
// PtrOrSpanOrNull
// PtrOrSpan
// Span
// Ptr
**************************************************************************/

// forward declarations
template <class T>
struct PtrOrSpanOrNull;
template <class T>
struct PtrOrSpan;
template <class T>
struct Span;
template <class T>
struct Ptr;

// XSpanInternalDummyArg: some type that is hard to match by accident
//   (this should result in efficient code and fully get optimized away)
#if 0
// too simple, can be matched by nullptr
class XSpanInternalDummyArgFake; // not implemented on purpose
typedef XSpanInternalDummyArgFake *XSpanInternalDummyArg;
#define XSpanInternalDummyArgInit nullptr
#elif __cplusplus >= 201103L && 1
// use an enum and a move constructor
struct XSpanInternalDummyArg final {
    enum DummyEnum {};
    explicit forceinline_constexpr XSpanInternalDummyArg(DummyEnum &&) noexcept {}
};
#define XSpanInternalDummyArgInit                                                                  \
    (XSPAN_NS(XSpanInternalDummyArg)(XSPAN_NS(XSpanInternalDummyArg)::DummyEnum{}))
#else
// use a class with a private constructor
struct XSpanInternalDummyArg final {
    static forceinline_constexpr XSpanInternalDummyArg make() noexcept {
        return XSpanInternalDummyArg();
    }
private:
    explicit forceinline_constexpr XSpanInternalDummyArg() noexcept {}
};
#define XSpanInternalDummyArgInit                                                                  \
    (XSPAN_NS(XSpanInternalDummyArg)(XSPAN_NS(XSpanInternalDummyArg)::make()))
#endif

XSPAN_NAMESPACE_END

#ifndef XSPAN_DELETED_FUNCTION
#define XSPAN_DELETED_FUNCTION = delete
#endif

// function/method constraints
#define XSPAN_REQUIRES_CONVERTIBLE_ONE_DIRECTION(From, To, RType)                                  \
    typename std::enable_if<XSPAN_NS(XSpan_is_convertible) < From, To>::value, RType > ::type
#define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType)                                      \
    typename std::enable_if<XSPAN_NS(XSpan_is_convertible) < A, B>::value ||                       \
        XSPAN_NS(XSpan_is_convertible)<B, A>::value,                                               \
        RType > ::type
// note: these use "T" and "U"
#define XSPAN_REQUIRES_CONVERTIBLE_R(RType) XSPAN_REQUIRES_CONVERTIBLE_ONE_DIRECTION(U, T, RType)
#define XSPAN_REQUIRES_CONVERTIBLE_A                                                               \
    XSPAN_REQUIRES_CONVERTIBLE_R(XSPAN_NS(XSpanInternalDummyArg)) = XSpanInternalDummyArgInit
#define XSPAN_REQUIRES_CONVERTIBLE_T XSPAN_REQUIRES_CONVERTIBLE_R(XSPAN_NS(XSpanInternalDummyArg))
// note: these use "T" and "U"
#define XSPAN_REQUIRES_SIZE_1_R(RType)                                                             \
    typename std::enable_if<XSPAN_NS(XSpan_is_convertible) < U, T>::value &&XSPAN_NS(              \
        ValueForSizeOf)<T>::value == 1 &&                                                          \
        XSPAN_NS(ValueForSizeOf)<U>::value == 1,                                                   \
        RType > ::type
#define XSPAN_REQUIRES_SIZE_1_A                                                                    \
    XSPAN_REQUIRES_SIZE_1_R(XSPAN_NS(XSpanInternalDummyArg)) = XSpanInternalDummyArgInit

#include "xspan_impl_ptr_or_null.h"
#include "xspan_impl_ptr_or_span.h"
#include "xspan_impl_span.h"
#include "xspan_impl_ptr.h"

#undef XSPAN_REQUIRES_CONVERTIBLE_ONE_DIRECTION
#undef XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION
#undef XSPAN_REQUIRES_CONVERTIBLE_A
#undef XSPAN_REQUIRES_CONVERTIBLE_R
#undef XSPAN_REQUIRES_CONVERTIBLE_T
#undef XSPAN_REQUIRES_SIZE_1_A
#undef XSPAN_REQUIRES_SIZE_1_R

#endif // WITH_XSPAN

/* vim:set ts=4 sw=4 et: */
