Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT
2 : // This file is part of AsmJit project <https://asmjit.com>
3 : //
4 : // See asmjit.h or LICENSE.md for license and copyright information
5 : // SPDX-License-Identifier: Zlib
6 :
7 : #ifndef ASMJIT_CORE_ZONE_H_INCLUDED
8 : #define ASMJIT_CORE_ZONE_H_INCLUDED
9 :
10 : #include "support.h"
11 :
12 : ASMJIT_BEGIN_NAMESPACE
13 :
14 : //! \addtogroup asmjit_zone
15 : //! \{
16 :
17 : //! Zone memory.
18 : //!
19 : //! Zone is an incremental memory allocator that allocates memory by simply incrementing a pointer. It allocates
20 : //! blocks of memory by using C's `malloc()`, but divides these blocks into smaller segments requested by calling
21 : //! `Zone::alloc()` and friends.
22 : //!
23 : //! Zone has no function to release the allocated memory. It has to be released all at once by calling `reset()`.
24 : //! If you need a more friendly allocator that also supports `release()`, consider using `Zone` with `ZoneAllocator`.
25 : class Zone {
26 : public:
27 : ASMJIT_NONCOPYABLE(Zone)
28 :
29 : //! \cond INTERNAL
30 :
31 : //! A single block of memory managed by `Zone`.
32 : struct Block {
33 : inline uint8_t* data() const noexcept {
34 110922 : return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(this) + sizeof(*this));
35 : }
36 :
37 : //! Link to the previous block.
38 : Block* prev;
39 : //! Link to the next block.
40 : Block* next;
41 : //! Size of the block.
42 : size_t size;
43 : };
44 :
45 : enum Limits : size_t {
46 : kBlockSize = sizeof(Block),
47 : kBlockOverhead = Globals::kAllocOverhead + kBlockSize,
48 :
49 : kMinBlockSize = 64, // The number is ridiculously small, but still possible.
50 : kMaxBlockSize = size_t(1) << (sizeof(size_t) * 8 - 4 - 1),
51 : kMinAlignment = 1,
52 : kMaxAlignment = 64
53 : };
54 :
55 : //! Pointer in the current block.
56 : uint8_t* _ptr;
57 : //! End of the current block.
58 : uint8_t* _end;
59 : //! Current block.
60 : Block* _block;
61 :
62 : union {
63 : struct {
64 : //! Default block size.
65 : size_t _blockSize : Support::bitSizeOf<size_t>() - 4;
66 : //! First block is temporary (ZoneTmp).
67 : size_t _isTemporary : 1;
68 : //! Block alignment (1 << alignment).
69 : size_t _blockAlignmentShift : 3;
70 : };
71 : size_t _packedData;
72 : };
73 :
74 : static ASMJIT_API const Block _zeroBlock;
75 :
76 : //! \endcond
77 :
78 : //! \name Construction & Destruction
79 : //! \{
80 :
81 : //! Creates a new Zone.
82 : //!
83 : //! The `blockSize` parameter describes the default size of the block. If the `size` parameter passed to `alloc()`
84 : //! is greater than the default size `Zone` will allocate and use a larger block, but it will not change the
85 : //! default `blockSize`.
86 : //!
87 : //! It's not required, but it's good practice to set `blockSize` to a reasonable value that depends on the usage
88 : //! of `Zone`. Greater block sizes are generally safer and perform better than unreasonably low block sizes.
89 109968 : inline explicit Zone(size_t blockSize, size_t blockAlignment = 1) noexcept {
90 54984 : _init(blockSize, blockAlignment, nullptr);
91 : }
92 :
93 : //! Creates a new Zone with a first block pointing to a `temporary` memory.
94 : inline Zone(size_t blockSize, size_t blockAlignment, const Support::Temporary& temporary) noexcept {
95 : _init(blockSize, blockAlignment, &temporary);
96 : }
97 :
98 : //! \overload
99 27492 : inline Zone(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept {
100 27492 : _init(blockSize, blockAlignment, temporary);
101 : }
102 :
103 : //! Moves an existing `Zone`.
104 : //!
105 : //! \note You cannot move an existing `ZoneTmp` as it uses embedded storage. Attempting to move `ZoneTmp` would
106 : //! result in assertion failure in debug mode and undefined behavior in release mode.
107 : inline Zone(Zone&& other) noexcept
108 : : _ptr(other._ptr),
109 : _end(other._end),
110 : _block(other._block),
111 : _packedData(other._packedData) {
112 : ASMJIT_ASSERT(!other.isTemporary());
113 : other._block = const_cast<Block*>(&_zeroBlock);
114 : other._ptr = other._block->data();
115 : other._end = other._block->data();
116 : }
117 :
118 : //! Destroys the `Zone` instance.
119 : //!
120 : //! This will destroy the `Zone` instance and release all blocks of memory allocated by it. It performs implicit
121 : //! `reset(ResetPolicy::kHard)`.
122 82476 : inline ~Zone() noexcept { reset(ResetPolicy::kHard); }
123 :
124 : ASMJIT_API void _init(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept;
125 :
126 : //! Resets the `Zone` invalidating all blocks allocated.
127 : //!
128 : //! See `Globals::ResetPolicy` for more details.
129 : ASMJIT_API void reset(ResetPolicy resetPolicy = ResetPolicy::kSoft) noexcept;
130 :
131 : //! \}
132 :
133 : //! \name Accessors
134 : //! \{
135 :
136 : //! Tests whether this `Zone` is actually a `ZoneTmp` that uses temporary memory.
137 : inline bool isTemporary() const noexcept { return _isTemporary != 0; }
138 :
139 : //! Returns the default block size.
140 110922 : inline size_t blockSize() const noexcept { return _blockSize; }
141 : //! Returns the default block alignment.
142 110922 : inline size_t blockAlignment() const noexcept { return size_t(1) << _blockAlignmentShift; }
143 : //! Returns remaining size of the current block.
144 331808 : inline size_t remainingSize() const noexcept { return (size_t)(_end - _ptr); }
145 :
146 : //! Returns the current zone cursor (dangerous).
147 : //!
148 : //! This is a function that can be used to get exclusive access to the current block's memory buffer.
149 : template<typename T = uint8_t>
150 2487597 : inline T* ptr() noexcept { return reinterpret_cast<T*>(_ptr); }
151 :
152 : //! Returns the end of the current zone block, only useful if you use `ptr()`.
153 : template<typename T = uint8_t>
154 2487597 : inline T* end() noexcept { return reinterpret_cast<T*>(_end); }
155 :
156 : //! Sets the current zone pointer to `ptr` (must be within the current block).
157 : template<typename T>
158 : inline void setPtr(T* ptr) noexcept {
159 : uint8_t* p = reinterpret_cast<uint8_t*>(ptr);
160 : ASMJIT_ASSERT(p >= _ptr && p <= _end);
161 2432277 : _ptr = p;
162 123 : }
163 :
164 : //! Sets the end zone pointer to `end` (must be within the current block).
165 : template<typename T>
166 : inline void setEnd(T* end) noexcept {
167 : uint8_t* p = reinterpret_cast<uint8_t*>(end);
168 : ASMJIT_ASSERT(p >= _ptr && p <= _end);
169 : _end = p;
170 : }
171 :
172 : //! \}
173 :
174 : //! \name Utilities
175 : //! \{
176 :
177 : inline void swap(Zone& other) noexcept {
178 : // This could lead to a disaster.
179 : ASMJIT_ASSERT(!this->isTemporary());
180 : ASMJIT_ASSERT(!other.isTemporary());
181 :
182 : std::swap(_ptr, other._ptr);
183 : std::swap(_end, other._end);
184 : std::swap(_block, other._block);
185 : std::swap(_packedData, other._packedData);
186 : }
187 :
188 : //! Aligns the current pointer to `alignment`.
189 : inline void align(size_t alignment) noexcept {
190 2542581 : _ptr = Support::min(Support::alignUp(_ptr, alignment), _end);
191 : }
192 :
193 : //! Ensures the remaining size is at least equal or greater than `size`.
194 : //!
195 : //! \note This function doesn't respect any alignment. If you need to ensure there is enough room for an aligned
196 : //! allocation you need to call `align()` before calling `ensure()`.
197 : inline Error ensure(size_t size) noexcept {
198 : if (size <= remainingSize())
199 : return kErrorOk;
200 : else
201 : return _alloc(0, 1) ? kErrorOk : DebugUtils::errored(kErrorOutOfMemory);
202 : }
203 :
204 54984 : inline void _assignBlock(Block* block) noexcept {
205 : size_t alignment = blockAlignment();
206 54984 : _ptr = Support::alignUp(block->data(), alignment);
207 54984 : _end = Support::alignDown(block->data() + block->size, alignment);
208 54984 : _block = block;
209 54984 : }
210 :
211 : inline void _assignZeroBlock() noexcept {
212 : Block* block = const_cast<Block*>(&_zeroBlock);
213 137460 : _ptr = block->data();
214 137460 : _end = block->data();
215 137460 : _block = block;
216 : }
217 :
218 : //! \}
219 :
220 : //! \name Allocation
221 : //! \{
222 :
223 : //! Allocates the requested memory specified by `size`.
224 : //!
225 : //! Pointer returned is valid until the `Zone` instance is destroyed or reset by calling `reset()`. If you plan to
226 : //! make an instance of C++ from the given pointer use placement `new` and `delete` operators:
227 : //!
228 : //! ```
229 : //! using namespace asmjit;
230 : //!
231 : //! class Object { ... };
232 : //!
233 : //! // Create Zone with default block size of approximately 65536 bytes.
234 : //! Zone zone(65536 - Zone::kBlockOverhead);
235 : //!
236 : //! // Create your objects using zone object allocating, for example:
237 : //! Object* obj = static_cast<Object*>( zone.alloc(sizeof(Object)) );
238 : //!
239 : //! if (!obj) {
240 : //! // Handle out of memory error.
241 : //! }
242 : //!
243 : //! // Placement `new` and `delete` operators can be used to instantiate it.
244 : //! new(obj) Object();
245 : //!
246 : //! // ... lifetime of your objects ...
247 : //!
248 : //! // To destroy the instance (if required).
249 : //! obj->~Object();
250 : //!
251 : //! // Reset or destroy `Zone`.
252 : //! zone.reset();
253 : //! ```
254 331808 : inline void* alloc(size_t size) noexcept {
255 331808 : if (ASMJIT_UNLIKELY(size > remainingSize()))
256 60 : return _alloc(size, 1);
257 :
258 : uint8_t* ptr = _ptr;
259 331748 : _ptr += size;
260 331748 : return static_cast<void*>(ptr);
261 : }
262 :
263 : //! Allocates the requested memory specified by `size` and `alignment`.
264 847503 : inline void* alloc(size_t size, size_t alignment) noexcept {
265 : ASMJIT_ASSERT(Support::isPowerOf2(alignment));
266 847503 : uint8_t* ptr = Support::alignUp(_ptr, alignment);
267 :
268 847503 : if (ptr >= _end || size > (size_t)(_end - ptr))
269 55542 : return _alloc(size, alignment);
270 :
271 791961 : _ptr = ptr + size;
272 791961 : return static_cast<void*>(ptr);
273 : }
274 :
275 : //! Allocates the requested memory specified by `size` without doing any checks.
276 : //!
277 : //! Can only be called if `remainingSize()` returns size at least equal to `size`.
278 : inline void* allocNoCheck(size_t size) noexcept {
279 : ASMJIT_ASSERT(remainingSize() >= size);
280 :
281 : uint8_t* ptr = _ptr;
282 : _ptr += size;
283 : return static_cast<void*>(ptr);
284 : }
285 :
286 : //! Allocates the requested memory specified by `size` and `alignment` without doing any checks.
287 : //!
288 : //! Performs the same operation as `Zone::allocNoCheck(size)` with `alignment` applied.
289 : inline void* allocNoCheck(size_t size, size_t alignment) noexcept {
290 : ASMJIT_ASSERT(Support::isPowerOf2(alignment));
291 :
292 : uint8_t* ptr = Support::alignUp(_ptr, alignment);
293 : ASMJIT_ASSERT(size <= (size_t)(_end - ptr));
294 :
295 : _ptr = ptr + size;
296 : return static_cast<void*>(ptr);
297 : }
298 :
299 : //! Allocates `size` bytes of zeroed memory. See `alloc()` for more details.
300 : ASMJIT_API void* allocZeroed(size_t size, size_t alignment = 1) noexcept;
301 :
302 : //! Like `alloc()`, but the return pointer is casted to `T*`.
303 : template<typename T>
304 : inline T* allocT(size_t size = sizeof(T), size_t alignment = alignof(T)) noexcept {
305 192444 : return static_cast<T*>(alloc(size, alignment));
306 : }
307 :
308 : //! Like `allocNoCheck()`, but the return pointer is casted to `T*`.
309 : template<typename T>
310 : inline T* allocNoCheckT(size_t size = sizeof(T), size_t alignment = alignof(T)) noexcept {
311 : return static_cast<T*>(allocNoCheck(size, alignment));
312 : }
313 :
314 : //! Like `allocZeroed()`, but the return pointer is casted to `T*`.
315 : template<typename T>
316 : inline T* allocZeroedT(size_t size = sizeof(T), size_t alignment = alignof(T)) noexcept {
317 273777 : return static_cast<T*>(allocZeroed(size, alignment));
318 : }
319 :
320 : //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`.
321 : template<typename T>
322 54984 : inline T* newT() noexcept {
323 54984 : void* p = alloc(sizeof(T), alignof(T));
324 54984 : if (ASMJIT_UNLIKELY(!p))
325 : return nullptr;
326 54984 : return new(p) T();
327 : }
328 :
329 : //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`.
330 : template<typename T, typename... Args>
331 326298 : inline T* newT(Args&&... args) noexcept {
332 326298 : void* p = alloc(sizeof(T), alignof(T));
333 326298 : if (ASMJIT_UNLIKELY(!p))
334 : return nullptr;
335 326298 : return new(p) T(std::forward<Args>(args)...);
336 : }
337 :
338 : //! \cond INTERNAL
339 : //!
340 : //! Internal alloc function used by other inlines.
341 : ASMJIT_API void* _alloc(size_t size, size_t alignment) noexcept;
342 : //! \endcond
343 :
344 : //! Helper to duplicate data.
345 : ASMJIT_API void* dup(const void* data, size_t size, bool nullTerminate = false) noexcept;
346 :
347 : //! Helper to duplicate data.
348 54984 : inline void* dupAligned(const void* data, size_t size, size_t alignment, bool nullTerminate = false) noexcept {
349 : align(alignment);
350 54984 : return dup(data, size, nullTerminate);
351 : }
352 :
353 : //! Helper to duplicate a formatted string, maximum size is 256 bytes.
354 : ASMJIT_API char* sformat(const char* str, ...) noexcept;
355 :
356 : //! \}
357 : };
358 :
359 : //! \ref Zone with `N` bytes of a static storage, used for the initial block.
360 : //!
361 : //! Temporary zones are used in cases where it's known that some memory will be required, but in many cases it won't
362 : //! exceed N bytes, so the whole operation can be performed without a dynamic memory allocation.
363 : template<size_t N>
364 : class ZoneTmp : public Zone {
365 : public:
366 : ASMJIT_NONCOPYABLE(ZoneTmp)
367 :
368 : //! Temporary storage, embedded after \ref Zone.
369 : struct Storage {
370 : char data[N];
371 : } _storage;
372 :
373 : //! Creates a temporary zone. Dynamic block size is specified by `blockSize`.
374 : inline explicit ZoneTmp(size_t blockSize, size_t blockAlignment = 1) noexcept
375 : : Zone(blockSize, blockAlignment, Support::Temporary(_storage.data, N)) {}
376 : };
377 :
378 : //! Zone-based memory allocator that uses an existing `Zone` and provides a `release()` functionality on top of it.
379 : //! It uses `Zone` only for chunks that can be pooled, and uses libc `malloc()` for chunks that are large.
380 : //!
381 : //! The advantage of ZoneAllocator is that it can allocate small chunks of memory really fast, and these chunks,
382 : //! when released, will be reused by consecutive calls to `alloc()`. Also, since ZoneAllocator uses `Zone`, you can
383 : //! turn any `Zone` into a `ZoneAllocator`, and use it in your `Pass` when necessary.
384 : //!
385 : //! ZoneAllocator is used by AsmJit containers to make containers having only few elements fast (and lightweight)
386 : //! and to allow them to grow and use dynamic blocks when require more storage.
387 : class ZoneAllocator {
388 : public:
389 : ASMJIT_NONCOPYABLE(ZoneAllocator)
390 :
391 : //! \cond INTERNAL
392 :
393 : // In short, we pool chunks of these sizes:
394 : // [32, 64, 96, 128, 192, 256, 320, 384, 448, 512]
395 :
396 : enum : uint32_t {
397 : //! How many bytes per a low granularity pool (has to be at least 16).
398 : kLoGranularity = 32,
399 : //! Number of slots of a low granularity pool.
400 : kLoCount = 4,
401 : //! Maximum size of a block that can be allocated in a low granularity pool.
402 : kLoMaxSize = kLoGranularity * kLoCount,
403 :
404 : //! How many bytes per a high granularity pool.
405 : kHiGranularity = 64,
406 : //! Number of slots of a high granularity pool.
407 : kHiCount = 6,
408 : //! Maximum size of a block that can be allocated in a high granularity pool.
409 : kHiMaxSize = kLoMaxSize + kHiGranularity * kHiCount,
410 :
411 : //! Alignment of every pointer returned by `alloc()`.
412 : kBlockAlignment = kLoGranularity
413 : };
414 :
415 : //! Single-linked list used to store unused chunks.
416 : struct Slot {
417 : //! Link to a next slot in a single-linked list.
418 : Slot* next;
419 : };
420 :
421 : //! A block of memory that has been allocated dynamically and is not part of block-list used by the allocator.
422 : //! This is used to keep track of all these blocks so they can be freed by `reset()` if not freed explicitly.
423 : struct DynamicBlock {
424 : DynamicBlock* prev;
425 : DynamicBlock* next;
426 : };
427 :
428 : //! \endcond
429 :
430 : //! \name Members
431 : //! \{
432 :
433 : //! Zone used to allocate memory that fits into slots.
434 : Zone* _zone;
435 : //! Indexed slots containing released memory.
436 : Slot* _slots[kLoCount + kHiCount];
437 : //! Dynamic blocks for larger allocations (no slots).
438 : DynamicBlock* _dynamicBlocks;
439 :
440 : //! \}
441 :
442 : //! \name Construction & Destruction
443 : //! \{
444 :
445 : //! Creates a new `ZoneAllocator`.
446 : //!
447 : //! \note To use it, you must first `init()` it.
448 27492 : inline ZoneAllocator() noexcept {
449 : memset(this, 0, sizeof(*this));
450 27492 : }
451 :
452 : //! Creates a new `ZoneAllocator` initialized to use `zone`.
453 54984 : inline explicit ZoneAllocator(Zone* zone) noexcept {
454 : memset(this, 0, sizeof(*this));
455 54984 : _zone = zone;
456 54984 : }
457 :
458 : //! Destroys the `ZoneAllocator`.
459 82476 : inline ~ZoneAllocator() noexcept { reset(); }
460 :
461 : //! Tests whether the `ZoneAllocator` is initialized (i.e. has `Zone`).
462 : inline bool isInitialized() const noexcept { return _zone != nullptr; }
463 :
464 : //! Convenience function to initialize the `ZoneAllocator` with `zone`.
465 : //!
466 : //! It's the same as calling `reset(zone)`.
467 : inline void init(Zone* zone) noexcept { reset(zone); }
468 :
469 : //! Resets this `ZoneAllocator` and also forget about the current `Zone` which is attached (if any). Reset
470 : //! optionally attaches a new `zone` passed, or keeps the `ZoneAllocator` in an uninitialized state, if
471 : //! `zone` is null.
472 : ASMJIT_API void reset(Zone* zone = nullptr) noexcept;
473 :
474 : //! \}
475 :
476 : //! \name Accessors
477 : //! \{
478 :
479 : //! Returns the assigned `Zone` of this allocator or null if this `ZoneAllocator` is not initialized.
480 850550 : inline Zone* zone() const noexcept { return _zone; }
481 :
482 : //! \}
483 :
484 : //! \cond
485 : //! \name Internals
486 : //! \{
487 :
488 : //! Returns the slot index to be used for `size`. Returns `true` if a valid slot has been written to `slot` and
489 : //! `allocatedSize` has been filled with slot exact size (`allocatedSize` can be equal or slightly greater than
490 : //! `size`).
491 : static inline bool _getSlotIndex(size_t size, uint32_t& slot) noexcept {
492 : ASMJIT_ASSERT(size > 0);
493 311167 : if (size > kHiMaxSize)
494 : return false;
495 :
496 297148 : if (size <= kLoMaxSize)
497 289711 : slot = uint32_t((size - 1) / kLoGranularity);
498 : else
499 7437 : slot = uint32_t((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount;
500 :
501 : return true;
502 : }
503 :
504 : //! \overload
505 2796496 : static inline bool _getSlotIndex(size_t size, uint32_t& slot, size_t& allocatedSize) noexcept {
506 : ASMJIT_ASSERT(size > 0);
507 2796496 : if (size > kHiMaxSize)
508 : return false;
509 :
510 2738298 : if (size <= kLoMaxSize) {
511 2608528 : slot = uint32_t((size - 1) / kLoGranularity);
512 2608528 : allocatedSize = Support::alignUp(size, kLoGranularity);
513 : }
514 : else {
515 129770 : slot = uint32_t((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount;
516 129770 : allocatedSize = Support::alignUp(size, kHiGranularity);
517 : }
518 :
519 : return true;
520 : }
521 :
522 : //! \}
523 : //! \endcond
524 :
525 : //! \name Allocation
526 : //! \{
527 :
528 : //! \cond INTERNAL
529 : ASMJIT_API void* _alloc(size_t size, size_t& allocatedSize) noexcept;
530 : ASMJIT_API void* _allocZeroed(size_t size, size_t& allocatedSize) noexcept;
531 : ASMJIT_API void _releaseDynamic(void* p, size_t size) noexcept;
532 : //! \endcond
533 :
534 : //! Allocates `size` bytes of memory, ideally from an available pool.
535 : //!
536 : //! \note `size` can't be zero, it will assert in debug mode in such case.
537 : inline void* alloc(size_t size) noexcept {
538 : ASMJIT_ASSERT(isInitialized());
539 : size_t allocatedSize;
540 587759 : return _alloc(size, allocatedSize);
541 : }
542 :
543 : //! Like `alloc(size)`, but provides a second argument `allocatedSize` that provides a way to know how big
544 : //! the block returned actually is. This is useful for containers to prevent growing too early.
545 : inline void* alloc(size_t size, size_t& allocatedSize) noexcept {
546 : ASMJIT_ASSERT(isInitialized());
547 1979162 : return _alloc(size, allocatedSize);
548 : }
549 :
550 : //! Like `alloc()`, but the return pointer is casted to `T*`.
551 : template<typename T>
552 : inline T* allocT(size_t size = sizeof(T)) noexcept {
553 : return static_cast<T*>(alloc(size));
554 : }
555 :
556 : //! Like `alloc(size)`, but returns zeroed memory.
557 : inline void* allocZeroed(size_t size) noexcept {
558 : ASMJIT_ASSERT(isInitialized());
559 : size_t allocatedSize;
560 82476 : return _allocZeroed(size, allocatedSize);
561 : }
562 :
563 : //! Like `alloc(size, allocatedSize)`, but returns zeroed memory.
564 : inline void* allocZeroed(size_t size, size_t& allocatedSize) noexcept {
565 : ASMJIT_ASSERT(isInitialized());
566 : return _allocZeroed(size, allocatedSize);
567 : }
568 :
569 : //! Like `allocZeroed()`, but the return pointer is casted to `T*`.
570 : template<typename T>
571 : inline T* allocZeroedT(size_t size = sizeof(T)) noexcept {
572 : return static_cast<T*>(allocZeroed(size));
573 : }
574 :
575 : //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`.
576 : template<typename T>
577 : inline T* newT() noexcept {
578 : void* p = allocT<T>();
579 : if (ASMJIT_UNLIKELY(!p))
580 : return nullptr;
581 : return new(p) T();
582 : }
583 : //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`.
584 : template<typename T, typename... Args>
585 147099 : inline T* newT(Args&&... args) noexcept {
586 : void* p = allocT<T>();
587 147099 : if (ASMJIT_UNLIKELY(!p))
588 : return nullptr;
589 147099 : return new(p) T(std::forward<Args>(args)...);
590 : }
591 :
592 : //! Releases the memory previously allocated by `alloc()`. The `size` argument has to be the same as used to call
593 : //! `alloc()` or `allocatedSize` returned by `alloc()`.
594 311167 : inline void release(void* p, size_t size) noexcept {
595 : ASMJIT_ASSERT(isInitialized());
596 : ASMJIT_ASSERT(p != nullptr);
597 : ASMJIT_ASSERT(size != 0);
598 :
599 : uint32_t slot;
600 : if (_getSlotIndex(size, slot)) {
601 352132 : static_cast<Slot*>(p)->next = static_cast<Slot*>(_slots[slot]);
602 352132 : _slots[slot] = static_cast<Slot*>(p);
603 : }
604 : else {
605 14019 : _releaseDynamic(p, size);
606 : }
607 311167 : }
608 :
609 : //! \}
610 : };
611 :
612 : //! \}
613 :
614 : ASMJIT_END_NAMESPACE
615 :
616 : #endif // ASMJIT_CORE_ZONE_H_INCLUDED
617 :
618 : #endif // __PLUMED_HAS_ASMJIT
|