1
0
mirror of https://github.com/upx/upx synced 2025-09-28 19:06:07 +08:00
upx/src/util/xspan_impl_common.h
Markus F.X.J. Oberhumer 8d41ae09c8 all: misc updates
2023-07-12 18:59:08 +02:00

466 lines
16 KiB
C++

/* xspan -- a minimally invasive checked memory smart pointer
This file is part of the UPX executable compressor.
Copyright (C) 1996-2023 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>
*/
/*************************************************************************
//
**************************************************************************/
#if CLANG_FORMAT_DUMMY_CLASS
class CSelf {
#endif
public:
typedef T element_type;
typedef typename std::add_lvalue_reference<T>::type reference;
typedef typename std::add_pointer<T>::type pointer;
typedef size_t size_type;
// befriend all
template <class>
friend struct PtrOrSpan;
template <class>
friend struct PtrOrSpanOrNull;
template <class>
friend struct Span;
#if XSPAN_CONFIG_ENABLE_IMPLICIT_CONVERSION
public:
operator pointer() const noexcept { return ptr; }
#endif
private:
pointer ptr; // current view into (base, base+size_in_bytes) iff base != nullptr
pointer base;
size_type size_in_bytes;
// debug - internal sanity check; also serves as pseudo-documentation
#if DEBUG
noinline void assertInvariants() const {
if __acc_cte (configRequirePtr)
assert(ptr != nullptr);
if __acc_cte (configRequireBase)
assert(base != nullptr);
if __acc_cte ((configRequirePtr || ptr != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(ptr, base, size_in_bytes);
}
#else
forceinline void assertInvariants() const noexcept {}
#endif
static inline pointer makeNotNull(pointer p) {
if very_unlikely (p == nullptr)
xspan_fail_nullptr();
return p;
}
// enforce config invariants at constructor time - static functions
static inline pointer makePtr(pointer p) {
if __acc_cte (configRequirePtr && p == nullptr)
xspan_fail_nullptr();
return p;
}
static inline pointer makeBase(pointer b) {
if __acc_cte (configRequireBase && b == nullptr)
xspan_fail_nullbase();
return b;
}
// inverse logic for ensuring valid pointers from existing objects
inline pointer ensurePtr() const {
if __acc_cte (!configRequirePtr && ptr == nullptr)
xspan_fail_nullptr();
return ptr;
}
inline pointer ensureBase() const {
if __acc_cte (!configRequireBase && base == nullptr)
xspan_fail_nullbase();
return base;
}
public:
#if DEBUG
~CSelf() noexcept {
try {
invalidate();
} catch (...) {
std::terminate();
}
}
#else
forceinline ~CSelf() noexcept {}
#endif
noinline void invalidate() {
assertInvariants();
// poison the pointer
ptr = (pointer) (upx_uintptr_t) 16; // point to non-null invalid address
// ptr = (pointer) (void *) &ptr; // point to self
base = ptr;
size_in_bytes = 0;
assertInvariants();
}
// constructors from pointers
CSelf(pointer first, XSpanCount count)
: ptr(makePtr(first)), base(makeBase(first)),
size_in_bytes(xspan_mem_size<T>(count.count)) {
assertInvariants();
}
CSelf(pointer first, XSpanSizeInBytes bytes)
: ptr(makePtr(first)), base(makeBase(first)),
size_in_bytes(xspan_mem_size<char>(bytes.size_in_bytes)) {
assertInvariants();
}
// enable this constructor only if the underlying type is char or void
template <class U>
CSelf(U *first, size_type count, XSPAN_REQUIRES_SIZE_1_A)
: ptr(makePtr(first)), base(makeBase(first)), size_in_bytes(xspan_mem_size<T>(count)) {
assertInvariants();
}
CSelf(pointer first, XSpanCount count, pointer base_)
: ptr(makePtr(first)), base(makeBase(base_)),
size_in_bytes(xspan_mem_size<T>(count.count)) {
// check invariants
if __acc_cte ((configRequirePtr || ptr != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(ptr, base, size_in_bytes);
// double sanity check
assertInvariants();
}
CSelf(pointer first, XSpanSizeInBytes bytes, pointer base_)
: ptr(makePtr(first)), base(makeBase(base_)),
size_in_bytes(xspan_mem_size<char>(bytes.size_in_bytes)) {
// check invariants
if __acc_cte ((configRequirePtr || ptr != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(ptr, base, size_in_bytes);
// double sanity check
assertInvariants();
}
// enable this constructor only if the underlying type is char or void
template <class U>
CSelf(pointer first, size_type count, U *base_, XSPAN_REQUIRES_SIZE_1_A)
: ptr(makePtr(first)), base(makeBase(base_)), size_in_bytes(xspan_mem_size<T>(count)) {
// check invariants
if __acc_cte ((configRequirePtr || ptr != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(ptr, base, size_in_bytes);
// double sanity check
assertInvariants();
}
#ifdef UPX_VERSION_HEX
// constructors from MemBuffer
CSelf(MemBuffer &mb)
: CSelf(makeNotNull((pointer) membuffer_get_void_ptr(mb)),
XSpanSizeInBytes(membuffer_get_size(mb))) {}
CSelf(pointer first, MemBuffer &mb)
: CSelf(first, XSpanSizeInBytes(membuffer_get_size(mb)),
makeNotNull((pointer) membuffer_get_void_ptr(mb))) {}
CSelf(std::nullptr_t, MemBuffer &) XSPAN_DELETED_FUNCTION;
#endif
// disable constructors from nullptr to catch compile-time misuse
private:
CSelf(std::nullptr_t, XSpanCount) XSPAN_DELETED_FUNCTION;
CSelf(std::nullptr_t, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
CSelf(const void *, XSpanCount, std::nullptr_t) XSPAN_DELETED_FUNCTION;
CSelf(std::nullptr_t, XSpanSizeInBytes) XSPAN_DELETED_FUNCTION;
CSelf(std::nullptr_t, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
CSelf(const void *, XSpanSizeInBytes, std::nullptr_t) XSPAN_DELETED_FUNCTION;
CSelf(std::nullptr_t, size_type) XSPAN_DELETED_FUNCTION;
CSelf(std::nullptr_t, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
CSelf(const void *, size_type, std::nullptr_t) XSPAN_DELETED_FUNCTION;
// unchecked constructor
protected:
enum ModeUnchecked { Unchecked };
CSelf(ModeUnchecked, pointer p, size_type bytes, pointer b)
: ptr(p), base(b), size_in_bytes(bytes) {
assertInvariants();
}
// unchecked assignment
Self &assign(ModeUnchecked, pointer p, size_type bytes, pointer b) {
ptr = p;
base = b;
size_in_bytes = bytes;
assertInvariants();
return *this;
}
#if 0
Self &assign(ModeUnchecked, const Self &other) {
ptr = other.ptr;
base = other.base;
size_in_bytes = other.size_in_bytes;
assertInvariants();
return *this;
}
#endif
public:
// assignment - here we can rely on invariants enforced at construction time by makePtr/makeBase
// NOTE: *this remains unmodified in case of failure
Self &assign(pointer other) {
assertInvariants();
other = makePtr(other);
if __acc_cte ((configRequirePtr || other != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(other, base, size_in_bytes);
// ok
ptr = other;
assertInvariants();
return *this;
}
Self &assign(const Self &other) {
assertInvariants();
other.assertInvariants();
if __acc_cte (!configRequireBase && base == nullptr) {
// magic 1: if base is unset, automatically set base/size_in_bytes from other
if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
(configRequireBase || other.base != nullptr))
xspan_check_range(other.ptr, other.base, other.size_in_bytes);
// ok
ptr = other.ptr;
base = other.base;
size_in_bytes = other.size_in_bytes;
} else {
// magic 2: assert same base (but ignore size_in_bytes !)
if __acc_cte (configRequireBase || other.base != nullptr)
if very_unlikely (base != other.base)
xspan_fail_not_same_base();
if __acc_cte ((configRequirePtr || other.ptr != nullptr) &&
(configRequireBase || base != nullptr))
xspan_check_range(other.ptr, base, size_in_bytes);
// ok
ptr = other.ptr;
}
assertInvariants();
return *this;
}
Self &operator=(pointer other) { return assign(other); }
Self &operator=(const Self &other) { return assign(other); }
// FIXME: this is not called??
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(Self &)
operator=(const CSelf<U> &other) {
// assert(0);
return assign(Self(other));
}
#ifdef UPX_VERSION_HEX
Self &operator=(MemBuffer &mb) { return assign(Self(mb)); }
#endif
Self subspan(ptrdiff_t offset, ptrdiff_t count) {
pointer begin = check_add(ptr, offset);
pointer end = check_add(begin, count);
if (begin <= end)
return Self(Unchecked, begin, (end - begin) * sizeof(T), begin);
else
return Self(Unchecked, end, (begin - end) * sizeof(T), end);
}
bool operator==(pointer other) const { return ptr == other; }
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator==(U *other) const {
return ptr == other;
}
bool operator!=(pointer other) const { return ptr != other; }
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator!=(U *other) const {
return ptr != other;
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator==(const PtrOrSpan<U> &other) const {
return ptr == other.ptr;
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator==(const PtrOrSpanOrNull<U> &other) const {
return ptr == other.ptr;
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator==(const Span<U> &other) const {
return ptr == other.ptr;
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator!=(const PtrOrSpan<U> &other) const {
return !(*this == other);
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator!=(const PtrOrSpanOrNull<U> &other) const {
return !(*this == other);
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator!=(const Span<U> &other) const {
return !(*this == other);
}
// check for notNull here
bool operator<(std::nullptr_t) const XSPAN_DELETED_FUNCTION;
bool operator<(pointer other) const { return ensurePtr() < makeNotNull(other); }
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator<(const PtrOrSpan<U> &other) const {
return ensurePtr() < other.ensurePtr();
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator<(const PtrOrSpanOrNull<U> &other) const {
return ensurePtr() < other.ensurePtr();
}
template <class U>
XSPAN_REQUIRES_CONVERTIBLE_R(bool)
operator<(const Span<U> &other) const {
return ensurePtr() < other.ensurePtr();
}
// dereference
reference operator*() const { return *check_deref(ptr); }
// array access
reference operator[](ptrdiff_t i) const { return *check_deref(ptr, i); }
// arrow operator
pointer operator->() const { return check_deref(ptr); }
Self &operator++() {
ptr = check_add(ptr, 1);
return *this;
}
Self operator++(int) {
Self tmp = *this;
++*this;
return tmp;
}
Self &operator--() {
ptr = check_add(ptr, -1);
return *this;
}
Self operator--(int) {
Self tmp = *this;
--*this;
return tmp;
}
Self &operator+=(ptrdiff_t n) {
ptr = check_add(ptr, n);
return *this;
}
Self &operator-=(ptrdiff_t n) {
ptr = check_add(ptr, -n);
return *this;
}
Self operator+(ptrdiff_t n) const {
pointer first = check_add(ptr, n);
return Self(Unchecked, first, size_in_bytes, base);
}
Self operator-(ptrdiff_t n) const {
pointer first = check_add(ptr, -n);
return Self(Unchecked, first, size_in_bytes, base);
}
private:
pointer check_deref(pointer p) const {
if __acc_cte (!configRequirePtr && p == nullptr)
xspan_fail_nullptr();
if __acc_cte (configRequireBase || base != nullptr)
xspan_check_range(p, base, size_in_bytes - sizeof(T));
assertInvariants();
return p;
}
pointer check_deref(pointer p, ptrdiff_t n) const {
if __acc_cte (!configRequirePtr && p == nullptr)
xspan_fail_nullptr();
xspan_mem_size_assert_ptrdiff<T>(n);
p += n;
if __acc_cte (configRequireBase || base != nullptr)
xspan_check_range(p, base, size_in_bytes - sizeof(T));
assertInvariants();
return p;
}
pointer check_add(pointer p, ptrdiff_t n) const {
if __acc_cte (!configRequirePtr && p == nullptr)
xspan_fail_nullptr();
xspan_mem_size_assert_ptrdiff<T>(n);
p += n;
if __acc_cte (configRequireBase || base != nullptr)
xspan_check_range(p, base, size_in_bytes);
assertInvariants();
return p;
}
// disable taking the address => force passing by reference
// [I'm not too sure about this design decision, but we can always allow it if needed]
Self *operator&() const XSPAN_DELETED_FUNCTION;
public: // raw access
pointer raw_ptr() const noexcept { return ptr; }
pointer raw_base() const noexcept { return base; }
size_type raw_size_in_bytes() const noexcept { return size_in_bytes; }
pointer raw_bytes(size_t bytes) const {
assertInvariants();
if (bytes > 0) {
if __acc_cte (!configRequirePtr && ptr == nullptr)
xspan_fail_nullptr();
if __acc_cte (configRequireBase || base != nullptr) {
xspan_check_range(ptr, base, size_in_bytes - bytes);
}
}
return ptr;
}
// like C++ std::span
pointer data() const noexcept { return ptr; }
pointer data(size_t bytes) const { return raw_bytes(bytes); } // UPX extra
// size_type size() const { return size_bytes() / sizeof(element_type); } // NOT USED
size_type size_bytes() const {
assertInvariants();
if __acc_cte (!configRequirePtr && ptr == nullptr)
return 0;
if __acc_cte (!configRequireBase && base == nullptr)
return 0;
const charptr begin = (const charptr) ptr;
const charptr end = (const charptr) base + size_in_bytes;
return end - begin;
}
#if CLANG_FORMAT_DUMMY_CLASS
}; // class
#endif
/* vim:set ts=4 sw=4 et: */