1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 Copyright (c) 2008-2017, Petr Kobalicek 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not 13 claim that you wrote the original software. If you use this software 14 in a product, an acknowledgment in the product documentation would be 15 appreciated but is not required. 16 2. Altered source versions must be plainly marked as such, and must not be 17 misrepresented as being the original software. 18 3. This notice may not be removed or altered from any source distribution. 19 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 20 #ifndef __PLUMED_asmjit_zone_h 21 #define __PLUMED_asmjit_zone_h 22 #ifdef __PLUMED_HAS_ASMJIT 23 #pragma GCC diagnostic push 24 #pragma GCC diagnostic ignored "-Wpedantic" 25 // [AsmJit] 26 // Complete x86/x64 JIT and Remote Assembler for C++. 27 // 28 // [License] 29 // Zlib - See LICENSE.md file in the package. 30 31 // [Guard] 32 #ifndef _ASMJIT_BASE_ZONE_H 33 #define _ASMJIT_BASE_ZONE_H 34 35 // [Dependencies] 36 #include "./utils.h" 37 38 // [Api-Begin] 39 #include "./asmjit_apibegin.h" 40 41 namespace PLMD { 42 namespace asmjit { 43 44 //! \addtogroup asmjit_base 45 //! \{ 46 47 // ============================================================================ 48 // [asmjit::Zone] 49 // ============================================================================ 50 51 //! Memory zone. 52 //! 53 //! Zone is an incremental memory allocator that allocates memory by simply 54 //! incrementing a pointer. It allocates blocks of memory by using standard 55 //! C `malloc`, but divides these blocks into smaller segments requested by 56 //! calling `Zone::alloc()` and friends. 57 //! 58 //! Zone has no function to release the allocated memory. It has to be released 59 //! all at once by calling `reset()`. If you need a more friendly allocator that 60 //! also supports `release()`, consider using \ref Zone with \ref ZoneHeap. 61 class Zone { 62 public: 63 //! \internal 64 //! 65 //! A single block of memory. 66 struct Block { 67 Block* prev; //!< Link to the previous block. 68 Block* next; //!< Link to the next block. 69 size_t size; //!< Size of the block. 70 uint8_t data[sizeof(void*)]; //!< Data. 71 }; 72 73 enum { 74 //! Zone allocator overhead. 75 kZoneOverhead = Globals::kAllocOverhead + static_cast<int>(sizeof(Block)) 76 }; 77 78 // -------------------------------------------------------------------------- 79 // [Construction / Destruction] 80 // -------------------------------------------------------------------------- 81 82 //! Create a new instance of `Zone` allocator. 83 //! 84 //! The `blockSize` parameter describes the default size of the block. If the 85 //! `size` parameter passed to `alloc()` is greater than the default size 86 //! `Zone` will allocate and use a larger block, but it will not change the 87 //! default `blockSize`. 88 //! 89 //! It's not required, but it's good practice to set `blockSize` to a 90 //! reasonable value that depends on the usage of `Zone`. Greater block sizes 91 //! are generally safer and perform better than unreasonably low values. 92 ASMJIT_API Zone(uint32_t blockSize, uint32_t blockAlignment = 0) noexcept; 93 94 //! Destroy the `Zone` instance. 95 //! 96 //! This will destroy the `Zone` instance and release all blocks of memory 97 //! allocated by it. It performs implicit `reset(true)`. 98 ASMJIT_API ~Zone() noexcept; 99 100 // -------------------------------------------------------------------------- 101 // [Reset] 102 // -------------------------------------------------------------------------- 103 104 //! Reset the `Zone` invalidating all blocks allocated. 105 //! 106 //! If `releaseMemory` is true all buffers will be released to the system. 107 ASMJIT_API void reset(bool releaseMemory = false) noexcept; 108 109 // -------------------------------------------------------------------------- 110 // [Accessors] 111 // -------------------------------------------------------------------------- 112 113 //! Get the default block size. getBlockSize()114 ASMJIT_INLINE uint32_t getBlockSize() const noexcept { return _blockSize; } 115 //! Get the default block alignment. getBlockAlignment()116 ASMJIT_INLINE uint32_t getBlockAlignment() const noexcept { return (uint32_t)1 << _blockAlignmentShift; } 117 //! Get remaining size of the current block. getRemainingSize()118 ASMJIT_INLINE size_t getRemainingSize() const noexcept { return (size_t)(_end - _ptr); } 119 120 //! Get the current zone cursor (dangerous). 121 //! 122 //! This is a function that can be used to get exclusive access to the current 123 //! block's memory buffer. getCursor()124 ASMJIT_INLINE uint8_t* getCursor() noexcept { return _ptr; } 125 //! Get the end of the current zone block, only useful if you use `getCursor()`. getEnd()126 ASMJIT_INLINE uint8_t* getEnd() noexcept { return _end; } 127 128 //! Set the current zone cursor to `p` (must match the current block). 129 //! 130 //! This is a counterpart of `getZoneCursor()`. setCursor(uint8_t * p)131 ASMJIT_INLINE void setCursor(uint8_t* p) noexcept { 132 ASMJIT_ASSERT(p >= _ptr && p <= _end); 133 _ptr = p; 134 } 135 136 // -------------------------------------------------------------------------- 137 // [Alloc] 138 // -------------------------------------------------------------------------- 139 140 //! Allocate `size` bytes of memory. 141 //! 142 //! Pointer returned is valid until the `Zone` instance is destroyed or reset 143 //! by calling `reset()`. If you plan to make an instance of C++ from the 144 //! given pointer use placement `new` and `delete` operators: 145 //! 146 //! ~~~ 147 //! using namespace asmjit; 148 //! 149 //! class Object { ... }; 150 //! 151 //! // Create Zone with default block size of approximately 65536 bytes. 152 //! Zone zone(65536 - Zone::kZoneOverhead); 153 //! 154 //! // Create your objects using zone object allocating, for example: 155 //! Object* obj = static_cast<Object*>( zone.alloc(sizeof(Object)) ); 156 // 157 //! if (!obj) { 158 //! // Handle out of memory error. 159 //! } 160 //! 161 //! // Placement `new` and `delete` operators can be used to instantiate it. 162 //! new(obj) Object(); 163 //! 164 //! // ... lifetime of your objects ... 165 //! 166 //! // To destroy the instance (if required). 167 //! obj->~Object(); 168 //! 169 //! // Reset or destroy `Zone`. 170 //! zone.reset(); 171 //! ~~~ alloc(size_t size)172 ASMJIT_INLINE void* alloc(size_t size) noexcept { 173 uint8_t* ptr = _ptr; 174 size_t remainingBytes = (size_t)(_end - ptr); 175 176 if (ASMJIT_UNLIKELY(remainingBytes < size)) 177 return _alloc(size); 178 179 _ptr += size; 180 ASMJIT_ASSERT(_ptr <= _end); 181 182 return static_cast<void*>(ptr); 183 } 184 185 //! Allocate `size` bytes without any checks. 186 //! 187 //! Can only be called if `getRemainingSize()` returns size at least equal 188 //! to `size`. allocNoCheck(size_t size)189 ASMJIT_INLINE void* allocNoCheck(size_t size) noexcept { 190 ASMJIT_ASSERT((size_t)(_end - _ptr) >= size); 191 192 uint8_t* ptr = _ptr; 193 _ptr += size; 194 return static_cast<void*>(ptr); 195 } 196 197 //! Allocate `size` bytes of zeroed memory. 198 //! 199 //! See \ref alloc() for more details. 200 ASMJIT_API void* allocZeroed(size_t size) noexcept; 201 202 //! Like `alloc()`, but the return pointer is casted to `T*`. 203 template<typename T> 204 ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { 205 return static_cast<T*>(alloc(size)); 206 } 207 208 //! Like `allocNoCheck()`, but the return pointer is casted to `T*`. 209 template<typename T> 210 ASMJIT_INLINE T* allocNoCheckT(size_t size = sizeof(T)) noexcept { 211 return static_cast<T*>(allocNoCheck(size)); 212 } 213 214 //! Like `allocZeroed()`, but the return pointer is casted to `T*`. 215 template<typename T> 216 ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { 217 return static_cast<T*>(allocZeroed(size)); 218 } 219 220 //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. 221 template<typename T> newT()222 ASMJIT_INLINE T* newT() noexcept { 223 void* p = alloc(sizeof(T)); 224 if (ASMJIT_UNLIKELY(!p)) 225 return nullptr; 226 return new(p) T(); 227 } 228 //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. 229 template<typename T, typename P1> newT(P1 p1)230 ASMJIT_INLINE T* newT(P1 p1) noexcept { 231 void* p = alloc(sizeof(T)); 232 if (ASMJIT_UNLIKELY(!p)) 233 return nullptr; 234 return new(p) T(p1); 235 } 236 237 //! \internal 238 ASMJIT_API void* _alloc(size_t size) noexcept; 239 240 //! Helper to duplicate data. 241 ASMJIT_API void* dup(const void* data, size_t size, bool nullTerminate = false) noexcept; 242 243 //! Helper to duplicate formatted string, maximum length is 256 bytes. 244 ASMJIT_API char* sformat(const char* str, ...) noexcept; 245 246 // -------------------------------------------------------------------------- 247 // [Members] 248 // -------------------------------------------------------------------------- 249 250 uint8_t* _ptr; //!< Pointer in the current block's buffer. 251 uint8_t* _end; //!< End of the current block's buffer. 252 Block* _block; //!< Current block. 253 254 #if ASMJIT_ARCH_64BIT 255 uint32_t _blockSize; //!< Default size of a newly allocated block. 256 uint32_t _blockAlignmentShift; //!< Minimum alignment of each block. 257 #else 258 uint32_t _blockSize : 29; //!< Default size of a newly allocated block. 259 uint32_t _blockAlignmentShift : 3; //!< Minimum alignment of each block. 260 #endif 261 }; 262 263 // ============================================================================ 264 // [asmjit::ZoneHeap] 265 // ============================================================================ 266 267 //! Zone-based memory allocator that uses an existing \ref Zone and provides 268 //! a `release()` functionality on top of it. It uses \ref Zone only for chunks 269 //! that can be pooled, and uses libc `malloc()` for chunks that are large. 270 //! 271 //! The advantage of ZoneHeap is that it can allocate small chunks of memory 272 //! really fast, and these chunks, when released, will be reused by consecutive 273 //! calls to `alloc()`. Also, since ZoneHeap uses \ref Zone, you can turn any 274 //! \ref Zone into a \ref ZoneHeap, and use it in your \ref Pass when necessary. 275 //! 276 //! ZoneHeap is used by AsmJit containers to make containers having only 277 //! few elements fast (and lightweight) and to allow them to grow and use 278 //! dynamic blocks when require more storage. 279 class ZoneHeap { 280 ASMJIT_NONCOPYABLE(ZoneHeap) 281 282 enum { 283 // In short, we pool chunks of these sizes: 284 // [32, 64, 96, 128, 192, 256, 320, 384, 448, 512] 285 286 //! How many bytes per a low granularity pool (has to be at least 16). 287 kLoGranularity = 32, 288 //! Number of slots of a low granularity pool. 289 kLoCount = 4, 290 //! Maximum size of a block that can be allocated in a low granularity pool. 291 kLoMaxSize = kLoGranularity * kLoCount, 292 293 //! How many bytes per a high granularity pool. 294 kHiGranularity = 64, 295 //! Number of slots of a high granularity pool. 296 kHiCount = 6, 297 //! Maximum size of a block that can be allocated in a high granularity pool. 298 kHiMaxSize = kLoMaxSize + kHiGranularity * kHiCount, 299 300 //! Alignment of every pointer returned by `alloc()`. 301 kBlockAlignment = kLoGranularity 302 }; 303 304 //! Single-linked list used to store unused chunks. 305 struct Slot { 306 //! Link to a next slot in a single-linked list. 307 Slot* next; 308 }; 309 310 //! A block of memory that has been allocated dynamically and is not part of 311 //! block-list used by the allocator. This is used to keep track of all these 312 //! blocks so they can be freed by `reset()` if not freed explicitly. 313 struct DynamicBlock { 314 DynamicBlock* prev; 315 DynamicBlock* next; 316 }; 317 318 // -------------------------------------------------------------------------- 319 // [Construction / Destruction] 320 // -------------------------------------------------------------------------- 321 322 //! Create a new `ZoneHeap`. 323 //! 324 //! NOTE: To use it, you must first `init()` it. ZoneHeap()325 ASMJIT_INLINE ZoneHeap() noexcept { 326 ::memset(this, 0, sizeof(*this)); 327 } 328 //! Create a new `ZoneHeap` initialized to use `zone`. ZoneHeap(Zone * zone)329 explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept { 330 ::memset(this, 0, sizeof(*this)); 331 _zone = zone; 332 } 333 //! Destroy the `ZoneHeap`. ~ZoneHeap()334 ASMJIT_INLINE ~ZoneHeap() noexcept { reset(); } 335 336 // -------------------------------------------------------------------------- 337 // [Init / Reset] 338 // -------------------------------------------------------------------------- 339 340 //! Get if the `ZoneHeap` is initialized (i.e. has `Zone`). isInitialized()341 ASMJIT_INLINE bool isInitialized() const noexcept { return _zone != nullptr; } 342 343 //! Convenience method to initialize the `ZoneHeap` with `zone`. 344 //! 345 //! It's the same as calling `reset(zone)`. init(Zone * zone)346 ASMJIT_INLINE void init(Zone* zone) noexcept { reset(zone); } 347 348 //! Reset this `ZoneHeap` and also forget about the current `Zone` which 349 //! is attached (if any). Reset optionally attaches a new `zone` passed, or 350 //! keeps the `ZoneHeap` in an uninitialized state, if `zone` is null. 351 ASMJIT_API void reset(Zone* zone = nullptr) noexcept; 352 353 // -------------------------------------------------------------------------- 354 // [Accessors] 355 // -------------------------------------------------------------------------- 356 357 //! Get the `Zone` the `ZoneHeap` is using, or null if it's not initialized. getZone()358 ASMJIT_INLINE Zone* getZone() const noexcept { return _zone; } 359 360 // -------------------------------------------------------------------------- 361 // [Utilities] 362 // -------------------------------------------------------------------------- 363 364 //! \internal 365 //! 366 //! Get the slot index to be used for `size`. Returns `true` if a valid slot 367 //! has been written to `slot` and `allocatedSize` has been filled with slot 368 //! exact size (`allocatedSize` can be equal or slightly greater than `size`). _getSlotIndex(size_t size,uint32_t & slot)369 static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot) noexcept { 370 ASMJIT_ASSERT(size > 0); 371 if (size > kHiMaxSize) 372 return false; 373 374 if (size <= kLoMaxSize) 375 slot = static_cast<uint32_t>((size - 1) / kLoGranularity); 376 else 377 slot = static_cast<uint32_t>((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; 378 379 return true; 380 } 381 382 //! \overload _getSlotIndex(size_t size,uint32_t & slot,size_t & allocatedSize)383 static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot, size_t& allocatedSize) noexcept { 384 ASMJIT_ASSERT(size > 0); 385 if (size > kHiMaxSize) 386 return false; 387 388 if (size <= kLoMaxSize) { 389 slot = static_cast<uint32_t>((size - 1) / kLoGranularity); 390 allocatedSize = Utils::alignTo(size, kLoGranularity); 391 } 392 else { 393 slot = static_cast<uint32_t>((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; 394 allocatedSize = Utils::alignTo(size, kHiGranularity); 395 } 396 397 return true; 398 } 399 400 // -------------------------------------------------------------------------- 401 // [Alloc / Release] 402 // -------------------------------------------------------------------------- 403 404 ASMJIT_API void* _alloc(size_t size, size_t& allocatedSize) noexcept; 405 ASMJIT_API void* _allocZeroed(size_t size, size_t& allocatedSize) noexcept; 406 ASMJIT_API void _releaseDynamic(void* p, size_t size) noexcept; 407 408 //! Allocate `size` bytes of memory, ideally from an available pool. 409 //! 410 //! NOTE: `size` can't be zero, it will assert in debug mode in such case. alloc(size_t size)411 ASMJIT_INLINE void* alloc(size_t size) noexcept { 412 ASMJIT_ASSERT(isInitialized()); 413 size_t allocatedSize; 414 return _alloc(size, allocatedSize); 415 } 416 417 //! Like `alloc(size)`, but provides a second argument `allocatedSize` that 418 //! provides a way to know how big the block returned actually is. This is 419 //! useful for containers to prevent growing too early. alloc(size_t size,size_t & allocatedSize)420 ASMJIT_INLINE void* alloc(size_t size, size_t& allocatedSize) noexcept { 421 ASMJIT_ASSERT(isInitialized()); 422 return _alloc(size, allocatedSize); 423 } 424 425 //! Like `alloc()`, but the return pointer is casted to `T*`. 426 template<typename T> 427 ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { 428 return static_cast<T*>(alloc(size)); 429 } 430 431 //! Like `alloc(size)`, but returns zeroed memory. allocZeroed(size_t size)432 ASMJIT_INLINE void* allocZeroed(size_t size) noexcept { 433 ASMJIT_ASSERT(isInitialized()); 434 435 size_t allocatedSize; 436 return _allocZeroed(size, allocatedSize); 437 } 438 439 //! Like `alloc(size, allocatedSize)`, but returns zeroed memory. allocZeroed(size_t size,size_t & allocatedSize)440 ASMJIT_INLINE void* allocZeroed(size_t size, size_t& allocatedSize) noexcept { 441 ASMJIT_ASSERT(isInitialized()); 442 443 return _allocZeroed(size, allocatedSize); 444 } 445 446 //! Like `allocZeroed()`, but the return pointer is casted to `T*`. 447 template<typename T> 448 ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { 449 return static_cast<T*>(allocZeroed(size)); 450 } 451 452 //! Release the memory previously allocated by `alloc()`. The `size` argument 453 //! has to be the same as used to call `alloc()` or `allocatedSize` returned 454 //! by `alloc()`. release(void * p,size_t size)455 ASMJIT_INLINE void release(void* p, size_t size) noexcept { 456 ASMJIT_ASSERT(isInitialized()); 457 458 ASMJIT_ASSERT(p != nullptr); 459 ASMJIT_ASSERT(size != 0); 460 461 uint32_t slot; 462 if (_getSlotIndex(size, slot)) { 463 //printf("RELEASING %p of size %d (SLOT %u)\n", p, int(size), slot); 464 static_cast<Slot*>(p)->next = static_cast<Slot*>(_slots[slot]); 465 _slots[slot] = static_cast<Slot*>(p); 466 } 467 else { 468 _releaseDynamic(p, size); 469 } 470 } 471 472 // -------------------------------------------------------------------------- 473 // [Members] 474 // -------------------------------------------------------------------------- 475 476 Zone* _zone; //!< Zone used to allocate memory that fits into slots. 477 Slot* _slots[kLoCount + kHiCount]; //!< Indexed slots containing released memory. 478 DynamicBlock* _dynamicBlocks; //!< Dynamic blocks for larger allocations (no slots). 479 }; 480 481 // ============================================================================ 482 // [asmjit::ZoneList<T>] 483 // ============================================================================ 484 485 //! \internal 486 template <typename T> 487 class ZoneList { 488 public: 489 ASMJIT_NONCOPYABLE(ZoneList<T>) 490 491 // -------------------------------------------------------------------------- 492 // [Link] 493 // -------------------------------------------------------------------------- 494 495 //! ZoneList node. 496 struct Link { 497 //! Get next node. getNextLink498 ASMJIT_INLINE Link* getNext() const noexcept { return _next; } 499 //! Get value. getValueLink500 ASMJIT_INLINE T getValue() const noexcept { return _value; } 501 //! Set value to `value`. setValueLink502 ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; } 503 504 Link* _next; 505 T _value; 506 }; 507 508 // -------------------------------------------------------------------------- 509 // [Appender] 510 // -------------------------------------------------------------------------- 511 512 //! Specialized appender that takes advantage of ZoneList structure. You must 513 //! initialize it and then call done(). 514 struct Appender { AppenderAppender515 ASMJIT_INLINE Appender(ZoneList<T>& list) noexcept { init(list); } 516 initAppender517 ASMJIT_INLINE void init(ZoneList<T>& list) noexcept { 518 pPrev = &list._first; 519 } 520 doneAppender521 ASMJIT_INLINE void done(ZoneList<T>& list) noexcept { 522 list._last = *pPrev; 523 *pPrev = nullptr; 524 } 525 appendAppender526 ASMJIT_INLINE void append(Link* node) noexcept { 527 *pPrev = node; 528 pPrev = &node->_next; 529 } 530 531 Link** pPrev; 532 }; 533 534 // -------------------------------------------------------------------------- 535 // [Construction / Destruction] 536 // -------------------------------------------------------------------------- 537 ZoneList()538 ASMJIT_INLINE ZoneList() noexcept : _first(nullptr), _last(nullptr) {} ~ZoneList()539 ASMJIT_INLINE ~ZoneList() noexcept {} 540 541 // -------------------------------------------------------------------------- 542 // [Data] 543 // -------------------------------------------------------------------------- 544 isEmpty()545 ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; } getFirst()546 ASMJIT_INLINE Link* getFirst() const noexcept { return _first; } getLast()547 ASMJIT_INLINE Link* getLast() const noexcept { return _last; } 548 549 // -------------------------------------------------------------------------- 550 // [Ops] 551 // -------------------------------------------------------------------------- 552 reset()553 ASMJIT_INLINE void reset() noexcept { 554 _first = nullptr; 555 _last = nullptr; 556 } 557 prepend(Link * link)558 ASMJIT_INLINE void prepend(Link* link) noexcept { 559 link->_next = _first; 560 if (!_first) _last = link; 561 _first = link; 562 } 563 append(Link * link)564 ASMJIT_INLINE void append(Link* link) noexcept { 565 link->_next = nullptr; 566 if (!_first) 567 _first = link; 568 else 569 _last->_next = link; 570 _last = link; 571 } 572 573 // -------------------------------------------------------------------------- 574 // [Members] 575 // -------------------------------------------------------------------------- 576 577 Link* _first; 578 Link* _last; 579 }; 580 581 // ============================================================================ 582 // [asmjit::ZoneVectorBase] 583 // ============================================================================ 584 585 //! \internal 586 class ZoneVectorBase { 587 public: ASMJIT_NONCOPYABLE(ZoneVectorBase)588 ASMJIT_NONCOPYABLE(ZoneVectorBase) 589 590 protected: 591 // -------------------------------------------------------------------------- 592 // [Construction / Destruction] 593 // -------------------------------------------------------------------------- 594 595 //! Create a new instance of `ZoneVectorBase`. 596 explicit ASMJIT_INLINE ZoneVectorBase() noexcept 597 : _data(nullptr), 598 _length(0), 599 _capacity(0) {} 600 601 // -------------------------------------------------------------------------- 602 // [Accessors] 603 // -------------------------------------------------------------------------- 604 605 public: 606 //! Get if the vector is empty. isEmpty()607 ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } 608 //! Get vector length. getLength()609 ASMJIT_INLINE size_t getLength() const noexcept { return _length; } 610 //! Get vector capacity. getCapacity()611 ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } 612 613 // -------------------------------------------------------------------------- 614 // [Ops] 615 // -------------------------------------------------------------------------- 616 617 //! Makes the vector empty (won't change the capacity or data pointer). clear()618 ASMJIT_INLINE void clear() noexcept { _length = 0; } 619 //! Reset the vector data and set its `length` to zero. reset()620 ASMJIT_INLINE void reset() noexcept { 621 _data = nullptr; 622 _length = 0; 623 _capacity = 0; 624 } 625 626 //! Truncate the vector to at most `n` items. truncate(size_t n)627 ASMJIT_INLINE void truncate(size_t n) noexcept { 628 _length = std::min(_length, n); 629 } 630 631 // -------------------------------------------------------------------------- 632 // [Memory Management] 633 // -------------------------------------------------------------------------- 634 635 protected: _release(ZoneHeap * heap,size_t sizeOfT)636 ASMJIT_INLINE void _release(ZoneHeap* heap, size_t sizeOfT) noexcept { 637 if (_data != nullptr) { 638 heap->release(_data, _capacity * sizeOfT); 639 reset(); 640 } 641 } 642 643 ASMJIT_API Error _grow(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; 644 ASMJIT_API Error _resize(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; 645 ASMJIT_API Error _reserve(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; 646 647 // -------------------------------------------------------------------------- 648 // [Members] 649 // -------------------------------------------------------------------------- 650 651 public: 652 void* _data; //!< Vector data. 653 size_t _length; //!< Length of the vector. 654 size_t _capacity; //!< Capacity of the vector. 655 }; 656 657 // ============================================================================ 658 // [asmjit::ZoneVector<T>] 659 // ============================================================================ 660 661 //! Template used to store and manage array of Zone allocated data. 662 //! 663 //! This template has these advantages over other std::vector<>: 664 //! - Always non-copyable (designed to be non-copyable, we want it). 665 //! - No copy-on-write (some implementations of STL can use it). 666 //! - Optimized for working only with POD types. 667 //! - Uses ZoneHeap, thus small vectors are basically for free. 668 template <typename T> 669 class ZoneVector : public ZoneVectorBase { 670 public: ASMJIT_NONCOPYABLE(ZoneVector<T>)671 ASMJIT_NONCOPYABLE(ZoneVector<T>) 672 673 // -------------------------------------------------------------------------- 674 // [Construction / Destruction] 675 // -------------------------------------------------------------------------- 676 677 //! Create a new instance of `ZoneVector<T>`. 678 explicit ASMJIT_INLINE ZoneVector() noexcept : ZoneVectorBase() {} 679 680 // -------------------------------------------------------------------------- 681 // [Accessors] 682 // -------------------------------------------------------------------------- 683 684 //! Get data. getData()685 ASMJIT_INLINE T* getData() noexcept { return static_cast<T*>(_data); } 686 //! \overload getData()687 ASMJIT_INLINE const T* getData() const noexcept { return static_cast<const T*>(_data); } 688 689 // -------------------------------------------------------------------------- 690 // [Ops] 691 // -------------------------------------------------------------------------- 692 693 //! Prepend `item` to the vector. prepend(ZoneHeap * heap,const T & item)694 Error prepend(ZoneHeap* heap, const T& item) noexcept { 695 if (ASMJIT_UNLIKELY(_length == _capacity)) 696 ASMJIT_PROPAGATE(grow(heap, 1)); 697 698 ::memmove(static_cast<T*>(_data) + 1, _data, _length * sizeof(T)); 699 ::memcpy(_data, &item, sizeof(T)); 700 701 _length++; 702 return kErrorOk; 703 } 704 705 //! Insert an `item` at the specified `index`. insert(ZoneHeap * heap,size_t index,const T & item)706 Error insert(ZoneHeap* heap, size_t index, const T& item) noexcept { 707 ASMJIT_ASSERT(index <= _length); 708 709 if (ASMJIT_UNLIKELY(_length == _capacity)) 710 ASMJIT_PROPAGATE(grow(heap, 1)); 711 712 T* dst = static_cast<T*>(_data) + index; 713 ::memmove(dst + 1, dst, _length - index); 714 ::memcpy(dst, &item, sizeof(T)); 715 716 _length++; 717 return kErrorOk; 718 } 719 720 //! Append `item` to the vector. append(ZoneHeap * heap,const T & item)721 Error append(ZoneHeap* heap, const T& item) noexcept { 722 if (ASMJIT_UNLIKELY(_length == _capacity)) 723 ASMJIT_PROPAGATE(grow(heap, 1)); 724 725 ::memcpy(static_cast<T*>(_data) + _length, &item, sizeof(T)); 726 727 _length++; 728 return kErrorOk; 729 } 730 concat(ZoneHeap * heap,const ZoneVector<T> & other)731 Error concat(ZoneHeap* heap, const ZoneVector<T>& other) noexcept { 732 size_t count = other._length; 733 if (_capacity - _length < count) 734 ASMJIT_PROPAGATE(grow(heap, count)); 735 736 ::memcpy(static_cast<T*>(_data) + _length, other._data, count * sizeof(T)); 737 738 _length += count; 739 return kErrorOk; 740 } 741 742 //! Prepend `item` to the vector (unsafe case). 743 //! 744 //! Can only be used together with `willGrow()`. If `willGrow(N)` returns 745 //! `kErrorOk` then N elements can be added to the vector without checking 746 //! if there is a place for them. Used mostly internally. prependUnsafe(const T & item)747 ASMJIT_INLINE void prependUnsafe(const T& item) noexcept { 748 ASMJIT_ASSERT(_length < _capacity); 749 T* data = static_cast<T*>(_data); 750 751 if (_length) 752 ::memmove(data + 1, data, _length * sizeof(T)); 753 754 ::memcpy(data, &item, sizeof(T)); 755 _length++; 756 } 757 758 //! Append `item` to the vector (unsafe case). 759 //! 760 //! Can only be used together with `willGrow()`. If `willGrow(N)` returns 761 //! `kErrorOk` then N elements can be added to the vector without checking 762 //! if there is a place for them. Used mostly internally. appendUnsafe(const T & item)763 ASMJIT_INLINE void appendUnsafe(const T& item) noexcept { 764 ASMJIT_ASSERT(_length < _capacity); 765 766 ::memcpy(static_cast<T*>(_data) + _length, &item, sizeof(T)); 767 _length++; 768 } 769 770 //! Concatenate all items of `other` at the end of the vector. concatUnsafe(const ZoneVector<T> & other)771 ASMJIT_INLINE void concatUnsafe(const ZoneVector<T>& other) noexcept { 772 size_t count = other._length; 773 ASMJIT_ASSERT(_capacity - _length >= count); 774 775 ::memcpy(static_cast<T*>(_data) + _length, other._data, count * sizeof(T)); 776 _length += count; 777 } 778 779 //! Get index of `val` or `kInvalidIndex` if not found. indexOf(const T & val)780 ASMJIT_INLINE size_t indexOf(const T& val) const noexcept { 781 const T* data = static_cast<const T*>(_data); 782 size_t length = _length; 783 784 for (size_t i = 0; i < length; i++) 785 if (data[i] == val) 786 return i; 787 788 return Globals::kInvalidIndex; 789 } 790 791 //! Get whether the vector contains `val`. contains(const T & val)792 ASMJIT_INLINE bool contains(const T& val) const noexcept { 793 return indexOf(val) != Globals::kInvalidIndex; 794 } 795 796 //! Remove item at index `i`. removeAt(size_t i)797 ASMJIT_INLINE void removeAt(size_t i) noexcept { 798 ASMJIT_ASSERT(i < _length); 799 800 T* data = static_cast<T*>(_data) + i; 801 _length--; 802 ::memmove(data, data + 1, _length - i); 803 } 804 805 //! Swap this pod-vector with `other`. swap(ZoneVector<T> & other)806 ASMJIT_INLINE void swap(ZoneVector<T>& other) noexcept { 807 Utils::swap(_length, other._length); 808 Utils::swap(_capacity, other._capacity); 809 Utils::swap(_data, other._data); 810 } 811 812 //! Get item at index `i` (const). getAt(size_t i)813 ASMJIT_INLINE const T& getAt(size_t i) const noexcept { 814 ASMJIT_ASSERT(i < _length); 815 return getData()[i]; 816 } 817 818 //! Get item at index `i`. 819 ASMJIT_INLINE T& operator[](size_t i) noexcept { 820 ASMJIT_ASSERT(i < _length); 821 return getData()[i]; 822 } 823 824 //! Get item at index `i`. 825 ASMJIT_INLINE const T& operator[](size_t i) const noexcept { 826 ASMJIT_ASSERT(i < _length); 827 return getData()[i]; 828 } 829 830 // -------------------------------------------------------------------------- 831 // [Memory Management] 832 // -------------------------------------------------------------------------- 833 834 //! Release the memory held by `ZoneVector<T>` back to the `heap`. release(ZoneHeap * heap)835 ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { _release(heap, sizeof(T)); } 836 837 //! Called to grow the buffer to fit at least `n` elements more. grow(ZoneHeap * heap,size_t n)838 ASMJIT_INLINE Error grow(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_grow(heap, sizeof(T), n); } 839 840 //! Resize the vector to hold `n` elements. 841 //! 842 //! If `n` is greater than the current length then the additional elements' 843 //! content will be initialized to zero. If `n` is less than the current 844 //! length then the vector will be truncated to exactly `n` elements. resize(ZoneHeap * heap,size_t n)845 ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_resize(heap, sizeof(T), n); } 846 847 //! Realloc internal array to fit at least `n` items. reserve(ZoneHeap * heap,size_t n)848 ASMJIT_INLINE Error reserve(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_reserve(heap, sizeof(T), n); } 849 850 ASMJIT_INLINE Error willGrow(ZoneHeap* heap, size_t n = 1) noexcept { 851 return _capacity - _length < n ? grow(heap, n) : static_cast<Error>(kErrorOk); 852 } 853 }; 854 855 // ============================================================================ 856 // [asmjit::ZoneBitVector] 857 // ============================================================================ 858 859 class ZoneBitVector { 860 public: 861 ASMJIT_NONCOPYABLE(ZoneBitVector) 862 863 //! Storage used to store a pack of bits (should by compatible with a machine word). 864 typedef uintptr_t BitWord; 865 enum { kBitsPerWord = static_cast<int>(sizeof(BitWord)) * 8 }; 866 _wordsPerBits(size_t nBits)867 static ASMJIT_INLINE size_t _wordsPerBits(size_t nBits) noexcept { 868 return ((nBits + kBitsPerWord) / kBitsPerWord) - 1; 869 } 870 871 // Return all bits zero if 0 and all bits set if 1. _patternFromBit(bool bit)872 static ASMJIT_INLINE BitWord _patternFromBit(bool bit) noexcept { 873 BitWord bitAsWord = static_cast<BitWord>(bit); 874 ASMJIT_ASSERT(bitAsWord == 0 || bitAsWord == 1); 875 return static_cast<BitWord>(0) - bitAsWord; 876 } 877 878 // -------------------------------------------------------------------------- 879 // [Construction / Destruction] 880 // -------------------------------------------------------------------------- 881 ZoneBitVector()882 explicit ASMJIT_INLINE ZoneBitVector() noexcept : 883 _data(nullptr), 884 _length(0), 885 _capacity(0) {} 886 887 // -------------------------------------------------------------------------- 888 // [Accessors] 889 // -------------------------------------------------------------------------- 890 891 //! Get if the bit-vector is empty (has no bits). isEmpty()892 ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } 893 //! Get a length of this bit-vector (in bits). getLength()894 ASMJIT_INLINE size_t getLength() const noexcept { return _length; } 895 //! Get a capacity of this bit-vector (in bits). getCapacity()896 ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } 897 898 //! Get data. getData()899 ASMJIT_INLINE BitWord* getData() noexcept { return _data; } 900 //! \overload getData()901 ASMJIT_INLINE const BitWord* getData() const noexcept { return _data; } 902 903 // -------------------------------------------------------------------------- 904 // [Ops] 905 // -------------------------------------------------------------------------- 906 clear()907 ASMJIT_INLINE void clear() noexcept { 908 _length = 0; 909 } 910 reset()911 ASMJIT_INLINE void reset() noexcept { 912 _data = nullptr; 913 _length = 0; 914 _capacity = 0; 915 } 916 truncate(size_t newLength)917 ASMJIT_INLINE void truncate(size_t newLength) noexcept { 918 _length = std::min(_length, newLength); 919 _clearUnusedBits(); 920 } 921 getAt(size_t index)922 ASMJIT_INLINE bool getAt(size_t index) const noexcept { 923 ASMJIT_ASSERT(index < _length); 924 925 size_t idx = index / kBitsPerWord; 926 size_t bit = index % kBitsPerWord; 927 return static_cast<bool>((_data[idx] >> bit) & 1); 928 } 929 setAt(size_t index,bool value)930 ASMJIT_INLINE void setAt(size_t index, bool value) noexcept { 931 ASMJIT_ASSERT(index < _length); 932 933 size_t idx = index / kBitsPerWord; 934 size_t bit = index % kBitsPerWord; 935 if (value) 936 _data[idx] |= static_cast<BitWord>(1) << bit; 937 else 938 _data[idx] &= ~(static_cast<BitWord>(1) << bit); 939 } 940 toggleAt(size_t index)941 ASMJIT_INLINE void toggleAt(size_t index) noexcept { 942 ASMJIT_ASSERT(index < _length); 943 944 size_t idx = index / kBitsPerWord; 945 size_t bit = index % kBitsPerWord; 946 _data[idx] ^= static_cast<BitWord>(1) << bit; 947 } 948 append(ZoneHeap * heap,bool value)949 ASMJIT_INLINE Error append(ZoneHeap* heap, bool value) noexcept { 950 size_t index = _length; 951 if (ASMJIT_UNLIKELY(index >= _capacity)) 952 return _append(heap, value); 953 954 size_t idx = index / kBitsPerWord; 955 size_t bit = index % kBitsPerWord; 956 957 if (bit == 0) 958 _data[idx] = static_cast<BitWord>(value) << bit; 959 else 960 _data[idx] |= static_cast<BitWord>(value) << bit; 961 962 _length++; 963 return kErrorOk; 964 } 965 966 ASMJIT_API Error fill(size_t fromIndex, size_t toIndex, bool value) noexcept; 967 and_(const ZoneBitVector & other)968 ASMJIT_INLINE void and_(const ZoneBitVector& other) noexcept { 969 BitWord* dst = _data; 970 const BitWord* src = other._data; 971 972 size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; 973 for (size_t i = 0; i < numWords; i++) 974 dst[i] = dst[i] & src[i]; 975 _clearUnusedBits(); 976 } 977 andNot(const ZoneBitVector & other)978 ASMJIT_INLINE void andNot(const ZoneBitVector& other) noexcept { 979 BitWord* dst = _data; 980 const BitWord* src = other._data; 981 982 size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; 983 for (size_t i = 0; i < numWords; i++) 984 dst[i] = dst[i] & ~src[i]; 985 _clearUnusedBits(); 986 } 987 or_(const ZoneBitVector & other)988 ASMJIT_INLINE void or_(const ZoneBitVector& other) noexcept { 989 BitWord* dst = _data; 990 const BitWord* src = other._data; 991 992 size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; 993 for (size_t i = 0; i < numWords; i++) 994 dst[i] = dst[i] | src[i]; 995 _clearUnusedBits(); 996 } 997 _clearUnusedBits()998 ASMJIT_INLINE void _clearUnusedBits() noexcept { 999 size_t idx = _length / kBitsPerWord; 1000 size_t bit = _length % kBitsPerWord; 1001 1002 if (!bit) return; 1003 _data[idx] &= (static_cast<BitWord>(1) << bit) - 1U; 1004 } 1005 1006 // -------------------------------------------------------------------------- 1007 // [Memory Management] 1008 // -------------------------------------------------------------------------- 1009 release(ZoneHeap * heap)1010 ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { 1011 if (_data != nullptr) { 1012 heap->release(_data, _capacity / 8); 1013 reset(); 1014 } 1015 } 1016 1017 ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t newLength, bool newBitsValue = false) noexcept { 1018 return _resize(heap, newLength, newLength, newBitsValue); 1019 } 1020 1021 ASMJIT_API Error _resize(ZoneHeap* heap, size_t newLength, size_t idealCapacity, bool newBitsValue) noexcept; 1022 ASMJIT_API Error _append(ZoneHeap* heap, bool value) noexcept; 1023 1024 // -------------------------------------------------------------------------- 1025 // [Members] 1026 // -------------------------------------------------------------------------- 1027 1028 BitWord* _data; //!< Bits. 1029 size_t _length; //!< Length of the bit-vector (in bits). 1030 size_t _capacity; //!< Capacity of the bit-vector (in bits). 1031 }; 1032 1033 // ============================================================================ 1034 // [asmjit::ZoneHashNode] 1035 // ============================================================================ 1036 1037 //! Node used by \ref ZoneHash<> template. 1038 //! 1039 //! You must provide function `bool eq(const Key& key)` in order to make 1040 //! `ZoneHash::get()` working. 1041 class ZoneHashNode { 1042 public: 1043 ASMJIT_INLINE ZoneHashNode(uint32_t hVal = 0) noexcept _hashNext(nullptr)1044 : _hashNext(nullptr), 1045 _hVal(hVal) {} 1046 1047 //! Next node in the chain, null if it terminates the chain. 1048 ZoneHashNode* _hashNext; 1049 //! Key hash. 1050 uint32_t _hVal; 1051 //! Should be used by Node that inherits ZoneHashNode, it aligns ZoneHashNode. 1052 uint32_t _customData; 1053 }; 1054 1055 // ============================================================================ 1056 // [asmjit::ZoneHashBase] 1057 // ============================================================================ 1058 1059 class ZoneHashBase { 1060 public: ASMJIT_NONCOPYABLE(ZoneHashBase)1061 ASMJIT_NONCOPYABLE(ZoneHashBase) 1062 1063 // -------------------------------------------------------------------------- 1064 // [Construction / Destruction] 1065 // -------------------------------------------------------------------------- 1066 1067 ASMJIT_INLINE ZoneHashBase(ZoneHeap* heap) noexcept { 1068 _heap = heap; 1069 _size = 0; 1070 _bucketsCount = 1; 1071 _bucketsGrow = 1; 1072 _data = _embedded; 1073 _embedded[0] = nullptr; 1074 } ~ZoneHashBase()1075 ASMJIT_INLINE ~ZoneHashBase() noexcept { reset(nullptr); } 1076 1077 // -------------------------------------------------------------------------- 1078 // [Reset] 1079 // -------------------------------------------------------------------------- 1080 isInitialized()1081 ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } 1082 ASMJIT_API void reset(ZoneHeap* heap) noexcept; 1083 1084 // -------------------------------------------------------------------------- 1085 // [Accessors] 1086 // -------------------------------------------------------------------------- 1087 1088 //! Get a `ZoneHeap` attached to this container. getHeap()1089 ASMJIT_INLINE ZoneHeap* getHeap() const noexcept { return _heap; } 1090 getSize()1091 ASMJIT_INLINE size_t getSize() const noexcept { return _size; } 1092 1093 // -------------------------------------------------------------------------- 1094 // [Ops] 1095 // -------------------------------------------------------------------------- 1096 1097 ASMJIT_API void _rehash(uint32_t newCount) noexcept; 1098 ASMJIT_API ZoneHashNode* _put(ZoneHashNode* node) noexcept; 1099 ASMJIT_API ZoneHashNode* _del(ZoneHashNode* node) noexcept; 1100 1101 // -------------------------------------------------------------------------- 1102 // [Members] 1103 // -------------------------------------------------------------------------- 1104 1105 ZoneHeap* _heap; //!< ZoneHeap used to allocate data. 1106 size_t _size; //!< Count of records inserted into the hash table. 1107 uint32_t _bucketsCount; //!< Count of hash buckets. 1108 uint32_t _bucketsGrow; //!< When buckets array should grow. 1109 1110 ZoneHashNode** _data; //!< Buckets data. 1111 ZoneHashNode* _embedded[1]; //!< Embedded data, used by empty hash tables. 1112 }; 1113 1114 // ============================================================================ 1115 // [asmjit::ZoneHash<Key, Node>] 1116 // ============================================================================ 1117 1118 //! Low-level hash table specialized for storing string keys and POD values. 1119 //! 1120 //! This hash table allows duplicates to be inserted (the API is so low 1121 //! level that it's up to you if you allow it or not, as you should first 1122 //! `get()` the node and then modify it or insert a new node by using `put()`, 1123 //! depending on the intention). 1124 template<typename Node> 1125 class ZoneHash : public ZoneHashBase { 1126 public: 1127 explicit ASMJIT_INLINE ZoneHash(ZoneHeap* heap = nullptr) noexcept ZoneHashBase(heap)1128 : ZoneHashBase(heap) {} ~ZoneHash()1129 ASMJIT_INLINE ~ZoneHash() noexcept {} 1130 1131 template<typename Key> get(const Key & key)1132 ASMJIT_INLINE Node* get(const Key& key) const noexcept { 1133 uint32_t hMod = key.hVal % _bucketsCount; 1134 Node* node = static_cast<Node*>(_data[hMod]); 1135 1136 while (node && !key.matches(node)) 1137 node = static_cast<Node*>(node->_hashNext); 1138 return node; 1139 } 1140 put(Node * node)1141 ASMJIT_INLINE Node* put(Node* node) noexcept { return static_cast<Node*>(_put(node)); } del(Node * node)1142 ASMJIT_INLINE Node* del(Node* node) noexcept { return static_cast<Node*>(_del(node)); } 1143 }; 1144 1145 //! \} 1146 1147 } // asmjit namespace 1148 } // namespace PLMD 1149 1150 // [Api-End] 1151 #include "./asmjit_apiend.h" 1152 1153 // [Guard] 1154 #endif // _ASMJIT_BASE_ZONE_H 1155 #pragma GCC diagnostic pop 1156 #endif // __PLUMED_HAS_ASMJIT 1157 #endif 1158