/* 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

XSPAN_NAMESPACE_BEGIN

/*************************************************************************
// Span
//
// invariants:
//   ptr != null, base != null
//   ptr is valid in base
**************************************************************************/

template <class T>
struct Span {
private:
#define CSelf Span
    typedef CSelf<T> Self;
    // core config
#if __cplusplus >= 201103L
    static constexpr bool configRequirePtr = true;
    static constexpr bool configRequireBase = true;
#else
    enum { configRequirePtr = true };
    enum { configRequireBase = true };
#endif

#include "xspan_impl_common.h"
public:
    // constructors from pointers
    CSelf(pointer first) XSPAN_DELETED_FUNCTION;
    CSelf(XSPAN_DEBUG_ARGS pointer first) XSPAN_DELETED_FUNCTION;

    // constructors
    CSelf(const Self &other)
        : XSPAN_DEBUG_OTHER ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    CSelf(XSPAN_DEBUG_ARGS const Self &other)
        : XSPAN_DEBUG_IMPL ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    template <class U>
    CSelf(const CSelf<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_OTHER ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    template <class U>
    CSelf(XSPAN_DEBUG_ARGS const CSelf<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_IMPL ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }

    // constructors from Span friends
#if XSPAN_CONFIG_ENABLE_SPAN_CONVERSION
    template <class U>
    CSelf(const PtrOrSpanOrNull<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_OTHER ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    template <class U>
    CSelf(XSPAN_DEBUG_ARGS const PtrOrSpanOrNull<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_IMPL ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    template <class U>
    CSelf(const PtrOrSpan<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_OTHER ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
    template <class U>
    CSelf(XSPAN_DEBUG_ARGS const PtrOrSpan<U> &other, XSPAN_REQUIRES_CONVERTIBLE_A)
        : XSPAN_DEBUG_IMPL ptr(other.ensurePtr()), base(other.ensureBase()),
          size_in_bytes(other.size_in_bytes) {
        assertInvariants();
    }
#endif

    // assignment from Span friends
#if XSPAN_CONFIG_ENABLE_SPAN_CONVERSION
    // TODO: use Unchecked to avoid double checks in both constructor and assignment
    template <class U>
    XSPAN_REQUIRES_CONVERTIBLE_R(Self &)
    operator=(const PtrOrSpanOrNull<U> &other) {
        if (other.base == nullptr)
            return assign(Self(other.ptr, size_in_bytes, base));
        return assign(Self(other.ptr, other.size_in_bytes, other.base));
    }
    template <class U>
    XSPAN_REQUIRES_CONVERTIBLE_R(Self &)
    operator=(const PtrOrSpan<U> &other) {
        if (other.base == nullptr)
            return assign(Self(other.ptr, size_in_bytes, base));
        return assign(Self(other.ptr, other.size_in_bytes, other.base));
    }
#endif

    // nullptr
    CSelf(std::nullptr_t) XSPAN_DELETED_FUNCTION;
    CSelf(std::nullptr_t, XSpanCount, const void *) XSPAN_DELETED_FUNCTION;
    CSelf(std::nullptr_t, XSpanSizeInBytes, const void *) XSPAN_DELETED_FUNCTION;
    CSelf(std::nullptr_t, size_type, const void *) XSPAN_DELETED_FUNCTION;
    CSelf(XSPAN_DEBUG_ARGS std::nullptr_t) XSPAN_DELETED_FUNCTION;
    CSelf(XSPAN_DEBUG_ARGS std::nullptr_t, XSpanCount, const void *) XSPAN_DELETED_FUNCTION;
    CSelf(XSPAN_DEBUG_ARGS std::nullptr_t, XSpanSizeInBytes, const void *) XSPAN_DELETED_FUNCTION;
    CSelf(XSPAN_DEBUG_ARGS std::nullptr_t, size_type, const void *) XSPAN_DELETED_FUNCTION;
    Self &operator=(std::nullptr_t) XSPAN_DELETED_FUNCTION;
#if 0
    // don't enable, this prevents generic usage
    bool operator==(std::nullptr_t) const XSPAN_DELETED_FUNCTION;
    bool operator!=(std::nullptr_t) const XSPAN_DELETED_FUNCTION;
#else
    bool operator==(std::nullptr_t) const {
        assertInvariants();
        return false;
    }
    bool operator!=(std::nullptr_t) const {
        assertInvariants();
        return true;
    }
#endif
#undef CSelf
};

// raw_bytes overload
template <class T>
inline typename Span<T>::pointer raw_bytes(const Span<T> &a, size_t size_in_bytes) {
    return a.raw_bytes(size_in_bytes);
}
template <class T>
inline typename Span<T>::pointer raw_index_bytes(const Span<T> &a, size_t index,
                                                 size_t size_in_bytes) {
    typedef typename Span<T>::element_type element_type;
    return a.raw_bytes(mem_size(sizeof(element_type), index, size_in_bytes)) + index;
}

/*************************************************************************
//
**************************************************************************/

XSPAN_NAMESPACE_END

#if !XSPAN_CONFIG_ENABLE_IMPLICIT_CONVERSION || 1

#define C XSPAN_NS(Span)
#define D XSPAN_NS(PtrOrSpanOrNull)
#define E XSPAN_NS(PtrOrSpan)
#include "xspan_fwd.h"
#undef C
#undef D
#undef E

#endif

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