/* membuffer.h -- This file is part of the UPX executable compressor. Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2025 Laszlo Molnar 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 Laszlo Molnar */ #pragma once // A MemBuffer allocates memory on the heap, and automatically // gets destructed when leaving scope or on exceptions. /************************************************************************* // MemBufferBase // provides some base functionality for treating a MemBuffer as a pointer **************************************************************************/ template class MemBufferBase { public: typedef T element_type; typedef typename std::add_lvalue_reference::type reference; typedef typename std::add_pointer::type pointer; typedef pointer iterator; typedef typename std::add_pointer::type const_iterator; typedef size_t size_type; // limited by UPX_RSIZE_MAX protected: static const size_t element_size = sizeof(element_type); protected: pointer ptr; size_type size_in_bytes; public: explicit inline MemBufferBase() noexcept : ptr(nullptr), size_in_bytes(0) {} forceinline ~MemBufferBase() noexcept {} // IMPORTANT NOTE: automatic conversion to underlying pointer // HINT: for fully bound-checked pointer use XSPAN_S from xspan.h operator pointer() const noexcept { return ptr; } // array access reference operator[](ptrdiff_t i) const may_throw { // NOTE: &array[SIZE] is *not* legal for containers like std::vector and MemBuffer ! if very_unlikely (i < 0 || mem_size(element_size, i) >= size_in_bytes) throwCantPack("MemBuffer invalid array index %td (%zu bytes)", i, size_in_bytes); return ptr[i]; } // dereference reference operator*() const DELETED_FUNCTION; // arrow operator pointer operator->() const DELETED_FUNCTION; iterator begin() const may_throw { if very_unlikely (ptr == nullptr) throwCantPack("MemBuffer begin() unexpected NULL ptr"); return ptr; } const_iterator cbegin() const may_throw { if very_unlikely (ptr == nullptr) throwCantPack("MemBuffer cbegin() unexpected NULL ptr"); return ptr; } iterator end() const may_throw { if very_unlikely (ptr == nullptr) throwCantPack("MemBuffer end() unexpected NULL ptr"); return ptr + size_in_bytes / element_size; } const_iterator cend() const may_throw { if very_unlikely (ptr == nullptr) throwCantPack("MemBuffer cend() unexpected NULL ptr"); return ptr + size_in_bytes / element_size; } // membuffer + n -> pointer template typename std::enable_if::value, pointer>::type operator+(U n) const may_throw { size_t bytes = mem_size(element_size, n); // check mem_size return raw_bytes(bytes) + n; // and check bytes } private: // membuffer - n -> pointer; not allowed - use raw_bytes() if needed template typename std::enable_if::value, pointer>::type operator-(U n) const DELETED_FUNCTION; public: // raw access pointer raw_ptr() const noexcept { return ptr; } size_type raw_size_in_bytes() const noexcept { return size_in_bytes; } pointer raw_bytes(size_t bytes) const may_throw { if (bytes > 0) { if very_unlikely (ptr == nullptr) throwCantPack("MemBuffer raw_bytes unexpected NULL ptr"); if very_unlikely (bytes > size_in_bytes) throwCantPack("MemBuffer raw_bytes invalid size"); } return ptr; } private: // 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] UPX_CXX_DISABLE_ADDRESS(MemBufferBase) }; /************************************************************************* // MemBufferBase global overloads **************************************************************************/ // global operators #if ALLOW_INT_PLUS_MEMBUFFER // rewrite "n + membuffer" to "membuffer + n" (member function) so that this will get checked above template inline typename std::enable_if::value, typename MemBufferBase::pointer>::type operator+(U n, const MemBufferBase &mbb) { return mbb + n; } #else // not allowed template inline typename std::enable_if::value, typename MemBufferBase::pointer>::type operator+(U n, const MemBufferBase &mbb) DELETED_FUNCTION; #endif // raw_bytes overload template inline typename MemBufferBase::pointer raw_bytes(const MemBufferBase &mbb, size_t size_in_bytes) { return mbb.raw_bytes(size_in_bytes); } template inline typename MemBufferBase::pointer raw_index_bytes(const MemBufferBase &mbb, size_t index, size_t size_in_bytes) { typedef typename MemBufferBase::element_type element_type; return mbb.raw_bytes(mem_size(sizeof(element_type), index, size_in_bytes)) + index; } // some more global overloads using a checked raw_bytes() call #if __cplusplus >= 201703L #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ std::enable_if_t || std::is_convertible_v, RType> #elif __cplusplus >= 201103L #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ typename std::enable_if< \ std::is_convertible::value || std::is_convertible::value, RType>::type #else #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ typename std::enable_if::value, RType>::type #endif #define XSPAN_FWD_C_IS_MEMBUFFER 1 #define C MemBufferBase #if WITH_XSPAN >= 1 #define D XSPAN_NS(Ptr) #endif #include "xspan_fwd.h" #undef XSPAN_FWD_C_IS_MEMBUFFER #undef C #undef D #undef XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION /************************************************************************* // MemBuffer **************************************************************************/ class MemBuffer final : public MemBufferBase { public: explicit inline MemBuffer() noexcept : MemBufferBase() {} explicit MemBuffer(upx_uint64_t bytes) may_throw; ~MemBuffer() noexcept; static unsigned getSizeForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; static unsigned getSizeForDecompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; noinline void alloc(upx_uint64_t bytes) may_throw; void allocForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; void allocForDecompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; noinline void checkState() const may_throw; noinline void dealloc() noexcept; // explicit conversion void *getVoidPtr() noexcept { return (void *) ptr; } const void *getVoidPtr() const noexcept { return (const void *) ptr; } unsigned getSizeInBytes() const noexcept { return size_in_bytes; } unsigned getSize() const noexcept { return size_in_bytes / element_size; } // util noinline void fill(size_t off, size_t bytes, int value) may_throw; forceinline void clear(size_t off, size_t bytes) may_throw { fill(off, bytes, 0); } forceinline void clear() may_throw { fill(0, size_in_bytes, 0); } // If the entire range [skip, skip+take) is inside the buffer, // then return &ptr[skip]; else throwCantPack(sprintf(errfmt, skip, take)). // This is similar to BoundedPtr, except only checks once. // skip == offset, take == size_in_bytes forceinline pointer subref(const char *errfmt, size_t skip, size_t take) may_throw { return (pointer) subref_impl(errfmt, skip, take); } template forceinline U subref_u(const char *errfmt, size_t skip) may_throw { COMPILE_TIME_ASSERT(std::is_pointer::value) return (U) subref_impl(errfmt, skip, sizeof(typename std::remove_pointer::type)); } template forceinline U subref_u(const char *errfmt, size_t skip, size_t take) may_throw { COMPILE_TIME_ASSERT(std::is_pointer::value) return (U) subref_impl(errfmt, skip, take); } private: noinline void *subref_impl(const char *errfmt, size_t skip, size_t take) may_throw; // static debug stats struct Stats { upx_std_atomic(upx_uint32_t) global_alloc_counter; upx_std_atomic(upx_uint32_t) global_dealloc_counter; #if WITH_THREADS // avoid link errors on some 32-bit platforms: undefined reference to __atomic_fetch_add_8 upx_std_atomic(size_t) global_total_bytes; // stats may overflow on 32-bit systems upx_std_atomic(size_t) global_total_active_bytes; #else upx_std_atomic(upx_uint64_t) global_total_bytes; upx_std_atomic(upx_uint64_t) global_total_active_bytes; #endif }; static Stats stats; #if DEBUG // debugging aid struct Debug { void *last_return_address_alloc; void *last_return_address_dealloc; void *last_return_address_fill; void *last_return_address_subref; Debug() noexcept { mem_clear(this); } }; Debug debug; #endif UPX_CXX_DISABLE_COPY_MOVE(MemBuffer) // disable copy and move UPX_CXX_DISABLE_NEW_DELETE_NO_VIRTUAL(MemBuffer) // disable dynamic allocation }; /* vim:set ts=4 sw=4 et: */