/src/upx/src/util/membuffer.h
Line | Count | Source |
1 | | /* membuffer.h -- |
2 | | |
3 | | This file is part of the UPX executable compressor. |
4 | | |
5 | | Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer |
6 | | Copyright (C) 1996-2025 Laszlo Molnar |
7 | | All Rights Reserved. |
8 | | |
9 | | UPX and the UCL library are free software; you can redistribute them |
10 | | and/or modify them under the terms of the GNU General Public License as |
11 | | published by the Free Software Foundation; either version 2 of |
12 | | the License, or (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program; see the file COPYING. |
21 | | If not, write to the Free Software Foundation, Inc., |
22 | | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
23 | | |
24 | | Markus F.X.J. Oberhumer Laszlo Molnar |
25 | | <markus@oberhumer.com> <ezerotven+github@gmail.com> |
26 | | */ |
27 | | |
28 | | #pragma once |
29 | | |
30 | | // A MemBuffer allocates memory on the heap, and automatically |
31 | | // gets destructed when leaving scope or on exceptions. |
32 | | |
33 | | /************************************************************************* |
34 | | // MemBufferBase |
35 | | // provides some base functionality for treating a MemBuffer as a pointer |
36 | | **************************************************************************/ |
37 | | |
38 | | template <class T> |
39 | | class MemBufferBase { |
40 | | public: |
41 | | typedef T element_type; |
42 | | typedef typename std::add_lvalue_reference<T>::type reference; |
43 | | typedef typename std::add_pointer<T>::type pointer; |
44 | | typedef unsigned size_type; // limited by UPX_RSIZE_MAX |
45 | | typedef pointer iterator; |
46 | | typedef typename std::add_pointer<const T>::type const_iterator; |
47 | | protected: |
48 | | static const size_t element_size = sizeof(element_type); |
49 | | |
50 | | protected: |
51 | | pointer ptr; |
52 | | size_type size_in_bytes; |
53 | | |
54 | | public: |
55 | 5.03M | explicit inline MemBufferBase() noexcept : ptr(nullptr), size_in_bytes(0) {} |
56 | 5.03M | forceinline ~MemBufferBase() noexcept {} |
57 | | |
58 | | // IMPORTANT NOTE: automatic conversion to underlying pointer |
59 | | // HINT: for fully bound-checked pointer use XSPAN_S from xspan.h |
60 | 80.2k | operator pointer() const noexcept { return ptr; } |
61 | | |
62 | | // array access |
63 | 4.75M | reference operator[](ptrdiff_t i) const may_throw { |
64 | | // NOTE: &array[SIZE] is *not* legal for containers like std::vector and MemBuffer ! |
65 | 4.75M | if very_unlikely (i < 0 || mem_size(element_size, i) + element_size > size_in_bytes) |
66 | 24.6k | throwCantPack("MemBuffer invalid array index %td (%u bytes)", i, size_in_bytes); |
67 | 4.72M | return ptr[i]; |
68 | 4.75M | } |
69 | | // dereference |
70 | | reference operator*() const DELETED_FUNCTION; |
71 | | // arrow operator |
72 | | pointer operator->() const DELETED_FUNCTION; |
73 | | |
74 | 98.4k | iterator begin() const may_throw { |
75 | 98.4k | if very_unlikely (ptr == nullptr) |
76 | 24.6k | throwCantPack("MemBuffer begin() unexpected NULL ptr"); |
77 | 73.8k | return ptr; |
78 | 98.4k | } |
79 | 73.8k | const_iterator cbegin() const may_throw { |
80 | 73.8k | if very_unlikely (ptr == nullptr) |
81 | 24.6k | throwCantPack("MemBuffer cbegin() unexpected NULL ptr"); |
82 | 49.2k | return ptr; |
83 | 73.8k | } |
84 | 910k | iterator end() const may_throw { |
85 | 910k | if very_unlikely (ptr == nullptr) |
86 | 24.6k | throwCantPack("MemBuffer end() unexpected NULL ptr"); |
87 | 885k | return ptr + size_in_bytes / element_size; |
88 | 910k | } |
89 | 73.8k | const_iterator cend() const may_throw { |
90 | 73.8k | if very_unlikely (ptr == nullptr) |
91 | 24.6k | throwCantPack("MemBuffer cend() unexpected NULL ptr"); |
92 | 49.2k | return ptr + size_in_bytes / element_size; |
93 | 73.8k | } |
94 | | |
95 | | // membuffer + n -> pointer |
96 | | template <class U> |
97 | | typename std::enable_if<std::is_integral<U>::value, pointer>::type operator+(U n) const |
98 | 1.12M | may_throw { |
99 | 1.12M | size_t bytes = mem_size(element_size, n); // check mem_size |
100 | 1.12M | return raw_bytes(bytes) + n; // and check bytes |
101 | 1.12M | } _ZNK13MemBufferBaseIhEplIjEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_ Line | Count | Source | 98 | 99.9k | may_throw { | 99 | 99.9k | size_t bytes = mem_size(element_size, n); // check mem_size | 100 | 99.9k | return raw_bytes(bytes) + n; // and check bytes | 101 | 99.9k | } |
_ZNK13MemBufferBaseIhEplIiEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_ Line | Count | Source | 98 | 114k | may_throw { | 99 | 114k | size_t bytes = mem_size(element_size, n); // check mem_size | 100 | 114k | return raw_bytes(bytes) + n; // and check bytes | 101 | 114k | } |
_ZNK13MemBufferBaseIhEplIyEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_ Line | Count | Source | 98 | 17.2k | may_throw { | 99 | 17.2k | size_t bytes = mem_size(element_size, n); // check mem_size | 100 | 17.2k | return raw_bytes(bytes) + n; // and check bytes | 101 | 17.2k | } |
_ZNK13MemBufferBaseIhEplIxEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_ Line | Count | Source | 98 | 4.57k | may_throw { | 99 | 4.57k | size_t bytes = mem_size(element_size, n); // check mem_size | 100 | 4.57k | return raw_bytes(bytes) + n; // and check bytes | 101 | 4.57k | } |
_ZNK13MemBufferBaseIhEplImEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_ Line | Count | Source | 98 | 885k | may_throw { | 99 | 885k | size_t bytes = mem_size(element_size, n); // check mem_size | 100 | 885k | return raw_bytes(bytes) + n; // and check bytes | 101 | 885k | } |
|
102 | | private: |
103 | | // membuffer - n -> pointer; not allowed - use raw_bytes() if needed |
104 | | template <class U> |
105 | | typename std::enable_if<std::is_integral<U>::value, pointer>::type operator-(U n) const |
106 | | DELETED_FUNCTION; |
107 | | |
108 | | public: // raw access |
109 | 24.6k | pointer raw_ptr() const noexcept { return ptr; } |
110 | 24.6k | size_type raw_size_in_bytes() const noexcept { return size_in_bytes; } |
111 | | |
112 | 1.86M | pointer raw_bytes(size_t bytes) const may_throw { |
113 | 1.86M | if (bytes > 0) { |
114 | 1.76M | if very_unlikely (ptr == nullptr) |
115 | 24.6k | throwCantPack("MemBuffer raw_bytes unexpected NULL ptr"); |
116 | 1.74M | if very_unlikely (bytes > size_in_bytes) |
117 | 418k | throwCantPack("MemBuffer raw_bytes invalid size"); |
118 | 1.74M | } |
119 | 1.41M | return ptr; |
120 | 1.86M | } |
121 | | |
122 | | private: |
123 | | // disable taking the address => force passing by reference |
124 | | // [I'm not too sure about this design decision, but we can always allow it if needed] |
125 | | UPX_CXX_DISABLE_ADDRESS(MemBufferBase) |
126 | | }; |
127 | | |
128 | | /************************************************************************* |
129 | | // MemBufferBase global overloads |
130 | | **************************************************************************/ |
131 | | |
132 | | // global operators |
133 | | #if ALLOW_INT_PLUS_MEMBUFFER |
134 | | // rewrite "n + membuffer" to "membuffer + n" (member function) so that this will get checked above |
135 | | template <class T, class U> |
136 | | inline typename std::enable_if<std::is_integral<U>::value, typename MemBufferBase<T>::pointer>::type |
137 | 52.5k | operator+(U n, const MemBufferBase<T> &mbb) { |
138 | 52.5k | return mbb + n; |
139 | 52.5k | } _ZplIhjENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_ Line | Count | Source | 137 | 30.7k | operator+(U n, const MemBufferBase<T> &mbb) { | 138 | 30.7k | return mbb + n; | 139 | 30.7k | } |
_ZplIhyENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_ Line | Count | Source | 137 | 17.2k | operator+(U n, const MemBufferBase<T> &mbb) { | 138 | 17.2k | return mbb + n; | 139 | 17.2k | } |
_ZplIhxENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_ Line | Count | Source | 137 | 4.57k | operator+(U n, const MemBufferBase<T> &mbb) { | 138 | 4.57k | return mbb + n; | 139 | 4.57k | } |
Unexecuted instantiation: _ZplIhmENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_ |
140 | | #else |
141 | | // not allowed |
142 | | template <class T, class U> |
143 | | inline typename std::enable_if<std::is_integral<U>::value, typename MemBufferBase<T>::pointer>::type |
144 | | operator+(U n, const MemBufferBase<T> &mbb) DELETED_FUNCTION; |
145 | | #endif |
146 | | |
147 | | // raw_bytes overload |
148 | | template <class T> |
149 | | inline typename MemBufferBase<T>::pointer raw_bytes(const MemBufferBase<T> &mbb, |
150 | 147k | size_t size_in_bytes) { |
151 | 147k | return mbb.raw_bytes(size_in_bytes); |
152 | 147k | } |
153 | | template <class T> |
154 | | inline typename MemBufferBase<T>::pointer raw_index_bytes(const MemBufferBase<T> &mbb, size_t index, |
155 | 0 | size_t size_in_bytes) { |
156 | 0 | typedef typename MemBufferBase<T>::element_type element_type; |
157 | 0 | return mbb.raw_bytes(mem_size(sizeof(element_type), index, size_in_bytes)) + index; |
158 | 0 | } |
159 | | |
160 | | // some more global overloads using a checked raw_bytes() call |
161 | | #if __cplusplus >= 201703L |
162 | | #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ |
163 | | std::enable_if_t<std::is_convertible_v<A *, B *> || std::is_convertible_v<B *, A *>, RType> |
164 | | #elif __cplusplus >= 201103L |
165 | | #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ |
166 | | typename std::enable_if< \ |
167 | | std::is_convertible<A *, B *>::value || std::is_convertible<B *, A *>::value, RType>::type |
168 | | #else |
169 | | #define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType) \ |
170 | | typename std::enable_if<std::is_same<A, B>::value, RType>::type |
171 | | #endif |
172 | | #define C MemBufferBase |
173 | | #define XSPAN_FWD_C_IS_MEMBUFFER 1 |
174 | | #if WITH_XSPAN >= 1 |
175 | | #define D XSPAN_NS(Ptr) |
176 | | #endif |
177 | | #include "xspan_fwd.h" |
178 | | #undef XSPAN_FWD_C_IS_MEMBUFFER |
179 | | #undef C |
180 | | #undef D |
181 | | #undef XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION |
182 | | |
183 | | /************************************************************************* |
184 | | // MemBuffer |
185 | | **************************************************************************/ |
186 | | |
187 | | class MemBuffer final : public MemBufferBase<byte> { |
188 | | public: |
189 | 4.89M | explicit inline MemBuffer() noexcept : MemBufferBase<byte>() {} |
190 | | explicit MemBuffer(upx_uint64_t bytes) may_throw; |
191 | | ~MemBuffer() noexcept; |
192 | | |
193 | | static unsigned getSizeForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; |
194 | | static unsigned getSizeForDecompression(unsigned uncompressed_size, unsigned extra = 0) |
195 | | may_throw; |
196 | | |
197 | | void alloc(upx_uint64_t bytes) may_throw; |
198 | | void allocForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; |
199 | | void allocForDecompression(unsigned uncompressed_size, unsigned extra = 0) may_throw; |
200 | | |
201 | | void dealloc() noexcept; |
202 | | void checkState() const may_throw; |
203 | | |
204 | | // explicit conversion |
205 | 250k | void *getVoidPtr() noexcept { return (void *) ptr; } |
206 | 14.2k | const void *getVoidPtr() const noexcept { return (const void *) ptr; } |
207 | 5.53k | unsigned getSizeInBytes() const noexcept { return size_in_bytes; } |
208 | 251k | unsigned getSize() const noexcept { return size_in_bytes; } // note: element_size == 1 |
209 | | |
210 | | // util |
211 | | noinline void fill(unsigned off, unsigned len, int value) may_throw; |
212 | 4 | forceinline void clear(unsigned off, unsigned len) may_throw { fill(off, len, 0); } |
213 | 83.3k | forceinline void clear() may_throw { fill(0, size_in_bytes, 0); } |
214 | | |
215 | | // If the entire range [skip, skip+take) is inside the buffer, |
216 | | // then return &ptr[skip]; else throwCantPack(sprintf(errfmt, skip, take)). |
217 | | // This is similar to BoundedPtr, except only checks once. |
218 | | // skip == offset, take == size_in_bytes |
219 | 98.5k | forceinline pointer subref(const char *errfmt, size_t skip, size_t take) may_throw { |
220 | 98.5k | return (pointer) subref_impl(errfmt, skip, take); |
221 | 98.5k | } |
222 | | |
223 | | private: |
224 | | void *subref_impl(const char *errfmt, size_t skip, size_t take) may_throw; |
225 | | |
226 | | // static debug stats |
227 | | struct Stats { |
228 | | upx_std_atomic(upx_uint32_t) global_alloc_counter; |
229 | | upx_std_atomic(upx_uint32_t) global_dealloc_counter; |
230 | | #if WITH_THREADS |
231 | | // avoid link errors on some 32-bit platforms: undefined reference to __atomic_fetch_add_8 |
232 | | upx_std_atomic(size_t) global_total_bytes; // stats may overflow on 32-bit systems |
233 | | upx_std_atomic(size_t) global_total_active_bytes; |
234 | | #else |
235 | | upx_std_atomic(upx_uint64_t) global_total_bytes; |
236 | | upx_std_atomic(upx_uint64_t) global_total_active_bytes; |
237 | | #endif |
238 | | }; |
239 | | static Stats stats; |
240 | | |
241 | | #if DEBUG |
242 | | // debugging aid |
243 | | struct Debug { |
244 | | void *last_return_address_alloc; |
245 | | void *last_return_address_dealloc; |
246 | | void *last_return_address_fill; |
247 | | void *last_return_address_subref; |
248 | | Debug() noexcept { mem_clear(this); } |
249 | | }; |
250 | | Debug debug; |
251 | | #endif |
252 | | |
253 | | UPX_CXX_DISABLE_COPY_MOVE(MemBuffer) // disable copy and move |
254 | | UPX_CXX_DISABLE_NEW_DELETE_NO_VIRTUAL(MemBuffer) // disable dynamic allocation |
255 | | }; |
256 | | |
257 | | /* vim:set ts=4 sw=4 et: */ |